A <- 1:5
B <- 11:15
names(A) <- A
names(B) <- B
A
B
View(anscombe)
lm(y3~x3, data = anscombe)
##-- now some "magic" to do the 4 regressions in a loop:
ff <- y ~ x
mods <- setNames(as.list(1:4), paste0("lm", 1:4))
for(i in 1:4) {
  ff[2:3] <- lapply(paste0(c("y","x"), i), as.name)
  ## or   ff[[2]] <- as.name(paste0("y", i))
  ##      ff[[3]] <- as.name(paste0("x", i))
  mods[[i]] <- lmi <- lm(ff, data = anscombe)
  print(anova(lmi))
}
## See how close they are (numerically!)
sapply(mods, coef)
lapply(mods, function(fm) coef(summary(fm)))
# Aggregate function
#Splits the data into subsets, computes summary statistics for each, and returns the result in a convenient form
testDF <- data.frame(v1 = c(1,3,5,7,8,3,5,NA,4,5,7,9),
                     v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99) )
by1 <- c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12)
by2 <- c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA)
aggregate(x = testDF, by = list(by1, by2), FUN = "mean")
## Now, do what you should have done in the first place: PLOTS
op <- par(mfrow = c(2, 2), mar = 0.1+c(4,4,1,1), oma =  c(0, 0, 2, 0))
for(i in 1:4) {
  ff[2:3] <- lapply(paste0(c("y","x"), i), as.name)
  plot(ff, data = anscombe, col = "red", pch = 21, bg = "orange", cex = 1.2,
       xlim = c(3, 19), ylim = c(3, 13))
  abline(mods[[i]], col = "blue")
}
mtext("Anscombe's 4 Regression data sets", outer = TRUE, cex = 1.5)
par(op)
x = c(TRUE, FALSE, FALSE)
typeof(x)  #logical (vector)
mode(x)
storage.mode(x)
y = 1:10
typeof(y)
mode(y)
storage.mode(y)
dim(y)
length(y)
z= list(1, TRUE, 'safs') #trying to get a list
typeof(z)
class(z)
z[3]
length(z)
dim(z)
quote(x+y)
as.list(quote(x + y))
1e3L #create constant
1L
cat(1+2)
'+'(1, 2)
x <- options()
x$prompt
match(NA, NaN)
match(NA, NA)
match(NaN, NaN)
x = array(1:8, c(2,4))
x
i=2
j=3
x[i]
x[i, j]
x[[i]]
x[[i, j]]
rownames(x)=c("a","b")
x
x = as.data.frame(x)
x
x["a",]
x[]
i <- matrix(1:4, 2, byrow = TRUE)
i
i[2,]
i[2, ,drop=FALSE] # keeping dimension 1 * n when selection a row
dim(i[2,])
dim(i[2, ,drop=FALSE])
# https://cran.r-project.org/doc/manuals/R-intro.pdf

help.start()
x <- rnorm(10000)
y <- rnorm(x)
plot(x, y)
hist(y)
ls()
rm(list=ls())
ls()
x <- 1:20
w <- 1 + sqrt(x)/2
dummy <- data.frame(x=x, y= x + rnorm(x)*w)
dummy 
# 4 Ordered and unordered factors
state <- c("tas", "sa", "qld", "nsw", "nsw", "nt", "wa", "wa",
"qld", "vic", "nsw", "vic", "qld", "qld", "sa", "tas",
"sa", "nt", "wa", "vic", "qld", "nsw", "nsw", "wa",
"sa", "act", "nsw", "vic", "vic", "act")
statef <- factor(state)
statef
levels(statef)
incomes <- c(60, 49, 40, 61, 64, 60, 59, 54, 62, 69, 70, 42, 56,
61, 61, 61, 58, 51, 48, 65, 49, 49, 41, 48, 52, 46,
59, 46, 58, 43)
incmeans <- tapply(incomes, statef, mean)
incmeans
# Arrays
a <- array(1:30, dim=c(2, 5,3))
a
x <- array(1:20, dim=c(4,5)) # Generate a 4 by 5 array.
x
i <- array(c(1:3,3:1), dim=c(3,2))
i
x[i] #get the 3 elements shown by i: (3, 1), (2, 2) and (1, 3)
help("<-")
# Back to http://zoonek2.free.fr/UNIX/48_R/02.html
x <- rnorm(10)
x
sort(x)
order(x)
x[order(x)]
x <- sample(1:5, 10, replace=T)
x
x[order(x)]
unique(x)
seq(0,10, length=11) == seq(0,10, by=1)
# Rep
rep(0, 10)
rep(1:5,3)
rep(1:5, each=3)
rep(1:5,2,each=3)
# Factor
x <- factor( sample(c("Yes", "No", "Perhaps"), 5, replace=T) )
x
# specify levels
l <- c("Yes", "No", "Perhaps")
x <- factor( sample(l, 5, replace=T), levels=l )
x
table(x)
# gl: Generate Factor Levels
gl(1,4)
gl(2,4)
gl(2,1,8)
gl(2,1,8, labels=c(T,F))
x <- gl(2,4)
x
y <- gl(2,1,8)
y
interaction(x,y)
data.frame(x,y, int=interaction(x,y))
# Cartesian product (toutes les possibilites)
x <- c("A", "B", "C")
y <- 1:2
z <- c("a", "b")
expand.grid(x,y,z)
x <- factor(c(3,4,5,1))
as.numeric( levels(x))
as.numeric( levels(x)[ x ] ) # proper way to convert from factor to numeric
# Data Frames
n <- 10
df <- data.frame( x=rnorm(n), y=sample(c(T,F),n,replace=T) )
str(df)
summary(df)
names(df);cat(rownames(df))
# Merge
merge(x, y)                 # INNER JOIN
merge(x, y, all.x = TRUE)   # LEFT JOIN
merge(x, y, all.y = TRUE)   # RIGHT JOIN
merge(x, y, all   = TRUE)   # OUTER JOIN
merge(a, b, by=c("y", "z")) # specify what to merge on
Error in fix.by(by.x, x) : 'by' must specify uniquely valid columns
# Regression
data(cars) 
#View(cars)

# Regression
lm.fit=lm( dist ~ speed, data=cars, na.action = na.exclude)
lm.fit
# Polynomial regression
lm.fit3 = lm( dist ~ poly(speed,3), data=cars)

#plot(lm.fit)
plot(cars$speed, cars$dist)
abline(lm.fit)
library(rpart.plot)
Loading required package: rpart
fit <- rpart(Survived ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked,
               data=train,
               method="class", 
               control=rpart.control(minsplit=2, cp=0))
Error in is.data.frame(data) : object 'train' not found
# Lists
h <- list()
h[["foo"]] <- 1
h[["bar"]] <- c("a", "b", "c")
h["bar"] == h[["bar"]] #h["bar"] is a list containing the vector
# Delete
h[["bar"]] <- NULL
m <- matrix( c(1,2,3,4), nrow=2 )
m
solve(m)
x <- matrix( c(6,7), nrow=2 )
solve(m, x)
?solve
n <- 1000
x1 <- factor( sample(1:3, n, replace=T), levels=1:3 )
x2 <- factor( sample(LETTERS[1:5], n, replace=T), levels=LETTERS[1:5] )
x3 <- factor( sample(c(F,T),n,replace=T), levels=c(F,T) )
d <- data.frame(x1,x2,x3)
r <- table(d)
r
ftable(d) #easier reading
# contingency table into a data.frame
n <- 100
k <- 10
x <- factor( sample(LETTERS[1:k], n, replace=T), levels=LETTERS[1:k] )
x
d <- table(x)
x2 = factor( rep(names(d),d), levels=names(d) )
x2
# apply
options(digits=4)
df <- data.frame(x=rnorm(20),y=rnorm(20),z=rnorm(20))
apply(df,2,mean)
rownames(df) <- LETTERS[1:20]
apply(df, 1, mean)
gl(2,10,20)
tapply(1:20, gl(2,10,20), sum) # tapply: 2nd argument used for grouping

by(1:20, gl(2,10,20), sum)
x <- list(a=rnorm(10), b=runif(100), c=rgamma(50,1))
sapply(x,sd) # sapply: apply FUN on each element of vector
lapply(x,sd) # lapply: same but returns list
# Exercise: Let x be a boolean vector. Count the number of sequences ("runs") of zeros (for instance, in 00101001010110, there are 6 runs: 00 0 00 0 0 0). Count the number of sequences of 1. Counth the total number of sequences. Same question for a factor with more tham two levels.
n <- 50
x <- sample(0:1, n, replace=T, p=c(.2,.8))
x
diff(x, lag=1)
#Let r be the return of a financial asset. The clustered return is the accumulated return for a sequence of returns of the same sign. The trend number is the number of steps in such a sequence. The average return is their ratio. Compute these quantities.
data(EuStockMarkets)
x <- EuStockMarkets
# We aren't interested in the spot prices, but in the returns
# return[i] = ( price[i] - price[i-1] ) / price[i-1]
y <- apply(x, 2, function (x) { diff(x)/x[-length(x)] })
# We normalize the data
z <- apply(y, 2, function (x) { (x-mean(x))/sd(x) })
# A single time series
r <- z[,1]
# The runs
f <- factor(cumsum(abs(diff(sign(r))))/2)
r <- r[-1]
accumulated.return <- tapply(r, f, sum)
trend.number <- table(f)
boxplot(abs(accumulated.return) ~ trend.number, col='pink',
        main="Accumulated return")
# Strings
print("Hello\n")

cat("Hello\n") #use cat
paste("Hello", "World", "!", sep="") #concatenate
paste("Hello ", " World", "!", sep="")
x <- 5
paste("x=", x)
cat("x=", x, "\n", sep="\n")
s <- c("Hello", " ", "World", "!")
paste(s)
paste(s, sep="")
paste(s, collapse="")
paste(1:3, "Hello World!", sep=":")
nchar("Hello World!")
s <- "Hello World"
substring(s, 4, 6)
s <- "foo-->bar-->baz"
strsplit(s, "-->")
# Regex
s <- "foo, bar, baz"
strsplit(s, ", *")
s <- apply(matrix(LETTERS[1:24], nr=4), 2, paste, collapse="")
s
grep("O", s)
grep("O", s, value=T)
regexpr("o", "Hello")
regexpr("o", c("Hello", "World!"))
s <- "foo    bar baz"
gsub(" ", "", s)   # Remove all the spaces
gsub(" +", " ", s)  # Remove multiple spaces and replace them by single spaces
#The "sub" is similar to "gsub" but only replaces the first occurrence.
s <- "foo bar baz"
sub(" ", "", s)
# Dates
as.Date("2005-05-15") #ISO 8601
as.Date("15/05/2005", format="%d/%m/%Y")
as.Date("15/05/05", format="%d/%m/%y")
cat("\n")
as.Date("01/02/03", format="%y/%m/%d")
as.Date("01/02/03", format="%y/%d/%m")
as.Date("01/02/03", format="%y/%m/%d") - as.Date("01/02/03", format="%y/%d/%m")
Sys.Date()
format(Sys.Date(), format="%A, %d %B %Y")
seq(as.Date("2005-01-01"), as.Date("2005-07-01"), by="month")
seq(as.Date("2005-01-01"), as.Date("2005-07-01"), by=31)
methods(class="Date")
as.POSIXlt("2005-05-15 21:45:17")
as.POSIXct(Sys.Date())
# Reading from dataframes
# option 1
  #d <- read.table("foo.txt")
  #d$Date <- as.Date( as.character( d$Date ) )
# option 2
  #read.table("foo.txt", colClasses=c("Date", "character", rep(10, "numeric")))
options(warn=1)
methods(plot)
# Import 

# d <- read.table("foo.txt", header=T, sep=",")
# d <- read.csv("txt.csv")
# d <- read.csv2("txt.csv")  # semicolon-separated file, with a
#                            # comma instead of the decimal point.
# d <- read.delim("foo.txt") # Tab-delimited file
# d <- read.fwf("txt.fwf")   # Fixed width fields
# Excel: this may be trickier: the missing values often appear as "#N/A!" and are mistaken for the start of a comment... You can try

# d <- read.table("foo.csv", header = TRUE, sep = ",", 
#                 na.strings = c("#N/A!", "NA", "@NA"), 
#                 quote = '"',
#                 comment.char = "")
#If your file only contains number, or only strings, it is wiser to store it in a matrix, not a data.frame. This is what the "scan" function does.
# A numeric matrix
  # x <- scan("foo.txt", sep=",")  # Gives a numeric vector
  # n <- scan("foo.txt", sep=",", nlines=1)
  # x <- matrix(x, nc=n)

# A vector of strings
  #x <- scan("foo.txt", what=character(0))
# Back tohttps://cran.r-project.org/doc/manuals/R-intro.pdf - Regression

  # fm05 <- lm(y ~ x1 + x2 + x3 + x4 + x5, data = production)
  # fm6 <- update(fm05, . ~ . + x6)
  # smf6 <- update(fm6, sqrt(.) ~ .)
# Spine (from help)
require(graphics)

op <- par(mfrow = c(2,1), mgp = c(2,.8,0), mar = 0.1+c(3,3,3,1))
n <- 9
x <- 1:n
y <- rnorm(n)
plot(x, y, main = paste("spline[fun](.) through", n, "points"))
lines(spline(x, y))
lines(spline(x, y, n = 210), col = 2)
# NA handling - http://thomasleeper.com/Rcourse/Tutorials/NA.html
g1 <- c(1, 2, NA, NA, NA, 6, 7)
g2 <- na.omit(g1)
g2
attributes(g2)$na.action
sum(g1)
sum(g1, na.rm = TRUE)
# Cor -> can eliminate only pair-wise NAs ()
x <- c(1, 2, 3, NA, 5, 7, 9)
y <- c(3, 2, 4, 5, 1, 3, 4)
z <- c(NA, 2, 3, 5, 4, 3, 4)
m <- data.frame(x, y, z)
m
cor(m)  # returns all NAs
   x  y  z
x  1 NA NA
y NA  1 NA
z NA NA  1
cor(m, use = "complete.obs")
       x       y       z
x 1.0000 0.34819 0.70957
y 0.3482 1.00000 0.04583
z 0.7096 0.04583 1.00000
cor(m, use = "pairwise.complete.obs")
       x      y      z
x 1.0000 0.2498 0.7096
y 0.2498 1.0000 0.4534
z 0.7096 0.4534 1.0000
# Defaut for lm is also casewise deletion
lm <- lm(y ~ x + z, data = m)
summary(lm)

Call:
lm(formula = y ~ x + z, data = m)

Residuals:
     2      3      5      6      7 
-0.632  1.711 -1.237 -0.447  0.605 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept)    3.316      3.399    0.98     0.43
x              0.289      0.408    0.71     0.55
z             -0.632      1.396   -0.45     0.70

Residual standard error: 1.65 on 2 degrees of freedom
  (2 observations deleted due to missingness)
Multiple R-squared:  0.203, Adjusted R-squared:  -0.594 
F-statistic: 0.254 on 2 and 2 DF,  p-value: 0.797
# Checking length of data used for regression
length(m$y)
[1] 7
length(lm$fitted)
[1] 5
# checking where missing data is
is.na(m) # only useful for small examples
         x     y     z
[1,] FALSE FALSE  TRUE
[2,] FALSE FALSE FALSE
[3,] FALSE FALSE FALSE
[4,]  TRUE FALSE FALSE
[5,] FALSE FALSE FALSE
[6,] FALSE FALSE FALSE
[7,] FALSE FALSE FALSE
# image
image(is.na(m), main = "Missing Values", xlab = "Observation", ylab = "Variable", 
    xaxt = "n", yaxt = "n", bty = "n")
axis(1, seq(0, 1, length.out = nrow(m)), 1:nrow(m), col = "white")
axis(2, c(0, 0.5, 1), names(m), col = "white", las = 2)

# to remove casewise:
m2 <- na.omit(m) # use new variable to keep original dataframe
m
m2
# Mean and Random imputation
x2 <- x
x2[is.na(x2)] <- mean(x2, na.rm = TRUE)
x
[1]  1  2  3 NA  5  7  9
x2
[1] 1.0 2.0 3.0 4.5 5.0 7.0 9.0
# Random imputation - conserve mean and variance. Sample rest of the values to fill NAs
x3 <- x
x3[is.na(x3)] <- sample(x3[!is.na(x3)], sum(is.na(x3)), TRUE)
x
[1]  1  2  3 NA  5  7  9
x3
[1] 1 2 3 5 5 7 9
# Saving R data http://thomasleeper.com/Rcourse/Tutorials/savingdata.html 
set.seed(1)
mydf <- data.frame(x = rnorm(10), y = rnorm(10), z = rnorm(10))
save(mydf, file = "saveddf.RData")
unlink("saveddf.RData")
# dput to have a readable format (e.g. for stack overflow)
dput(mydf)
structure(list(x = c(-0.626453810742332, 0.183643324222082, -0.835628612410047, 
1.59528080213779, 0.329507771815361, -0.820468384118015, 0.487429052428485, 
0.738324705129217, 0.575781351653492, -0.305388387156356), y = c(1.51178116845085, 
0.389843236411431, -0.621240580541804, -2.2146998871775, 1.12493091814311, 
-0.0449336090152309, -0.0161902630989461, 0.943836210685299, 
0.821221195098089, 0.593901321217509), z = c(0.918977371608218, 
0.782136300731067, 0.0745649833651906, -1.98935169586337, 0.61982574789471, 
-0.0561287395290008, -0.155795506705329, -1.47075238389927, -0.47815005510862, 
0.417941560199702)), .Names = c("x", "y", "z"), row.names = c(NA, 
-10L), class = "data.frame")
dput(mydf, "saveddf.txt")
mydf2 <- dget("saveddf.txt")
mydf2
mydf==mydf2
          x     y     z
 [1,] FALSE FALSE FALSE
 [2,] FALSE FALSE FALSE
 [3,] FALSE FALSE  TRUE
 [4,] FALSE  TRUE FALSE
 [5,] FALSE FALSE FALSE
 [6,] FALSE FALSE FALSE
 [7,] FALSE FALSE FALSE
 [8,] FALSE FALSE FALSE
 [9,] FALSE FALSE FALSE
[10,]  TRUE FALSE FALSE
unlink("saveddf.text")
# csv
write.csv(mydf, file = "saveddf.csv")
unlink("savedf.csv")
# Dataframe rearrangement
set.seed(50)
mydf <- data.frame(a = rep(1:2, each = 10), b = rep(1:4, times = 5), c = rnorm(20), 
    d = rnorm(20), e = sample(1:20, 20, FALSE))
head(mydf)
# manual order change
head(mydf[, c("c", "d", "e", "a", "b")])
# mydf <- mydf[, c(3, 4, 5, 1, 2)]
# using order
order(mydf$e)
 [1]  2 16 18 19 20 14  3  4  5 12  1 11 13 15  7  8 10  9  6 17
head(mydf[order(mydf$e), ])
# Subset
mydf[mydf$a == 1, ]
mydf[mydf$a == 1 & mydf$b > 2, ]
subset(mydf, a == 1 & b > 2)
subset(mydf, select = c("a", "b"))
# Splitting 
split(mydf, mydf$a)
$`1`

$`2`
NA
split(mydf, list(mydf$a, mydf$b))
$`1.1`

$`2.1`

$`1.2`

$`2.2`

$`1.3`

$`2.3`

$`1.4`

$`2.4`
lapply(split(mydf, mydf$a), summary)
$`1`
       a           b              c                d                e        
 Min.   :1   Min.   :1.00   Min.   :-1.728   Min.   :-1.590   Min.   : 1.00  
 1st Qu.:1   1st Qu.:1.25   1st Qu.:-0.779   1st Qu.:-0.359   1st Qu.: 8.25  
 Median :1   Median :2.00   Median :-0.122   Median : 0.193   Median :13.00  
 Mean   :1   Mean   :2.30   Mean   :-0.244   Mean   : 0.299   Mean   :12.10  
 3rd Qu.:1   3rd Qu.:3.00   3rd Qu.: 0.483   3rd Qu.: 0.568   3rd Qu.:16.75  
 Max.   :1   Max.   :4.00   Max.   : 0.976   Max.   : 2.668   Max.   :19.00  

$`2`
       a           b              c                d                 e        
 Min.   :2   Min.   :1.00   Min.   :-1.166   Min.   :-1.1304   Min.   : 2.00  
 1st Qu.:2   1st Qu.:2.00   1st Qu.:-0.488   1st Qu.:-0.6338   1st Qu.: 4.25  
 Median :2   Median :3.00   Median :-0.343   Median : 0.2961   Median : 8.00  
 Mean   :2   Mean   :2.70   Mean   :-0.268   Mean   : 0.0793   Mean   : 8.90  
 3rd Qu.:2   3rd Qu.:3.75   3rd Qu.: 0.108   3rd Qu.: 0.4137   3rd Qu.:12.75  
 Max.   :2   Max.   :4.00   Max.   : 0.555   Max.   : 1.8397   Max.   :20.00  
lapply(split(mydf, mydf$a), summary)
$`1`
       a           b              c                d                e        
 Min.   :1   Min.   :1.00   Min.   :-1.728   Min.   :-1.590   Min.   : 1.00  
 1st Qu.:1   1st Qu.:1.25   1st Qu.:-0.779   1st Qu.:-0.359   1st Qu.: 8.25  
 Median :1   Median :2.00   Median :-0.122   Median : 0.193   Median :13.00  
 Mean   :1   Mean   :2.30   Mean   :-0.244   Mean   : 0.299   Mean   :12.10  
 3rd Qu.:1   3rd Qu.:3.00   3rd Qu.: 0.483   3rd Qu.: 0.568   3rd Qu.:16.75  
 Max.   :1   Max.   :4.00   Max.   : 0.976   Max.   : 2.668   Max.   :19.00  

$`2`
       a           b              c                d                 e        
 Min.   :2   Min.   :1.00   Min.   :-1.166   Min.   :-1.1304   Min.   : 2.00  
 1st Qu.:2   1st Qu.:2.00   1st Qu.:-0.488   1st Qu.:-0.6338   1st Qu.: 4.25  
 Median :2   Median :3.00   Median :-0.343   Median : 0.2961   Median : 8.00  
 Mean   :2   Mean   :2.70   Mean   :-0.268   Mean   : 0.0793   Mean   : 8.90  
 3rd Qu.:2   3rd Qu.:3.75   3rd Qu.: 0.108   3rd Qu.: 0.4137   3rd Qu.:12.75  
 Max.   :2   Max.   :4.00   Max.   : 0.555   Max.   : 1.8397   Max.   :20.00  
# sampling
s <- sample(1:nrow(mydf), 5, F) #no replacement
s
[1] 18  1 12 15  6
mydf[s,]
# test set
mydf[-s, ]
# Option 2
s2 <- rbinom(nrow(mydf), 1, 0.2)
s2
 [1] 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0
mydf[s2,]
mydf[-s2,]
library(car)

Attaching package: ‘car’

The following object is masked from ‘package:dplyr’:

    recode
b <- 1:20
#h <- recode(b, "1:5=1: 6:10=2; else=NA") # incredibly this creates an error
e <- recode(b, "1:5=1; 6:10=2; else=NA")
e
 [1]  1  1  1  1  1  2  2  2  2  2 NA NA NA NA NA NA NA NA NA NA
f <- recode(b, "lo:5=1; 6:10=2; 11:15=3; 16:hi=4; else=NA")
f
 [1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4
e <- recode(h, "NA=99")
e
 [1]  1  2  3  4  5 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
# Reconding on multiple variables
i <- expand.grid(1:4, 1:2)
i
interaction(i$Var1, i$Var2) # creates all possible combinations
[1] 1.1 2.1 3.1 4.1 1.2 2.2 3.2 4.2
Levels: 1.1 2.1 3.1 4.1 1.2 2.2 3.2 4.2
set.seed(1)
n <- 30
mydf <- data.frame(x1 = rbinom(n, 1, 0.5), x2 = rbinom(n, 1, 0.1), x3 = rbinom(n, 
    1, 0.5), x4 = rbinom(n, 1, 0.8), x5 = 1, x6 = sample(c(0, 1, NA), n, TRUE))
str(mydf)
'data.frame':   30 obs. of  6 variables:
 $ x1: int  0 0 1 1 0 1 1 1 1 0 ...
 $ x2: int  0 0 0 0 0 0 0 0 0 0 ...
 $ x3: int  1 0 0 0 1 0 0 1 0 1 ...
 $ x4: int  1 1 1 0 1 1 1 1 0 1 ...
 $ x5: num  1 1 1 1 1 1 1 1 1 1 ...
 $ x6: num  NA 1 1 0 NA 1 1 0 0 1 ...
mydf$x1 + mydf$x2 - mydf$x3 # vector operations
 [1] -1  0  1  1 -1  1  1  0  1 -1  0 -1  1  0  1 -1  0  1 -1  0  1 -1  1  0 -1  0 -1  0  1
[30]  0
with(mydf, x1+x2-x3)
 [1] -1  0  1  1 -1  1  1  0  1 -1  0 -1  1  0  1 -1  0  1 -1  0  1 -1  1  0 -1  0 -1  0  1
[30]  0
rowSums(mydf)
 [1] NA  3  4  2 NA  4  4  4  2  4  3  3  3  2 NA  4  5  4 NA  5 NA  4  3  2 NA  3  3 NA  3
[30] NA
rowSums(mydf, na.rm = T)
 [1] 3 3 4 2 3 4 4 4 2 4 3 3 3 2 3 4 5 4 2 5 2 4 3 2 3 3 3 2 3 2
data.frame(1:n, rowSums(mydf, na.rm = T))
rowMeans(mydf)
 [1]     NA 0.5000 0.6667 0.3333     NA 0.6667 0.6667 0.6667 0.3333 0.6667 0.5000 0.5000
[13] 0.5000 0.3333     NA 0.6667 0.8333 0.6667     NA 0.8333     NA 0.6667 0.5000 0.3333
[25]     NA 0.5000 0.5000     NA 0.5000     NA
apply(mydf, 1, var) # 2nd argument: 1 for rows, 2 for columns, c(1, 2)  rows & columns.
 [1]     NA 0.3000 0.2667 0.2667     NA 0.2667 0.2667 0.2667 0.2667 0.2667 0.3000 0.3000
[13] 0.3000 0.2667     NA 0.2667 0.1667 0.2667     NA 0.1667     NA 0.2667 0.3000 0.2667
[25]     NA 0.3000 0.3000     NA 0.3000     NA
apply(mydf, 2, var)
    x1     x2     x3     x4     x5     x6 
0.2575 0.0000 0.2483 0.1437 0.0000     NA 
sapply(mydf, var) # over list or vector
    x1     x2     x3     x4     x5     x6 
0.2575 0.0000 0.2483 0.1437 0.0000     NA 
newvar
 [1] 3 2 0 0 3 0 0 1 0 3 2 3 0 1 0 3 1 0 2 1 0 3 0 2 3 2 3 2 0 2
newvar[mydf$x1 == 1] <- with(mydf, x2 + x3)
number of items to replace is not a multiple of replacement length
# Matrices
set.seed(1)
a <- rnorm(100)
quantile(a, c(0.025, 0.975))
  2.5%  97.5% 
-1.671  1.797 
quantile(a, seq(0, 1, by = 0.1))
     0%     10%     20%     30%     40%     50%     60%     70%     80%     90%    100% 
-2.2147 -1.0527 -0.6139 -0.3753 -0.0767  0.1139  0.3771  0.5812  0.7713  1.1811  2.4016 
summary(as.logical(rbinom(1000, 1, 0.5)))
   Mode   FALSE    TRUE 
logical     492     508 
summary(factor(a))
   -2.2146998871775   -1.98935169586337   -1.80495862889104   -1.52356680042976 
                  1                   1                   1                   1 
  -1.47075238389927   -1.37705955682861   -1.27659220845804    -1.2536334002391 
                  1                   1                   1                   1 
  -1.22461261489836   -1.12936309608079   -1.04413462631653  -0.934097631644252 
                  1                   1                   1                   1 
 -0.835628612410047  -0.820468384118015  -0.743273208882405  -0.709946430921815 
                  1                   1                   1                   1 
  -0.70749515696212   -0.68875569454952  -0.626453810742332  -0.621240580541804 
                  1                   1                   1                   1 
 -0.612026393250771  -0.589520946188072  -0.573265414236886  -0.568668732818502 
                  1                   1                   1                   1 
  -0.54252003099165   -0.47815005510862  -0.473400636439312  -0.443291873218433 
                  1                   1                   1                   1 
  -0.41499456329968  -0.394289953710349  -0.367221476466509  -0.305388387156356 
                  1                   1                   1                   1 
 -0.304183923634301  -0.253361680136508  -0.164523596253587  -0.155795506705329 
                  1                   1                   1                   1 
 -0.135178615123832  -0.135054603880824  -0.112346212150228  -0.102787727342996 
                  1                   1                   1                   1 
-0.0593133967111857 -0.0561287395290008 -0.0538050405829051 -0.0449336090152309 
                  1                   1                   1                   1 
-0.0392400027331692 -0.0161902630989461 0.00110535163162413  0.0280021587806661 
                  1                   1                   1                   1 
 0.0743413241516641  0.0745649833651906   0.153253338211898   0.183643324222082 
                  1                   1                   1                   1 
  0.188792299514343   0.267098790772231   0.291446235517463   0.329507771815361 
                  1                   1                   1                   1 
  0.332950371213518   0.341119691424425    0.36458196213683   0.370018809916288 
                  1                   1                   1                   1 
  0.387671611559369   0.389843236411431   0.398105880367068   0.417941560199702 
                  1                   1                   1                   1 
  0.475509528899663   0.487429052428485   0.556663198673657   0.558486425565304 
                  1                   1                   1                   1 
  0.569719627442413   0.575781351653492   0.593901321217509   0.593946187628422 
                  1                   1                   1                   1 
  0.610726353489055    0.61982574789471   0.689739362450777   0.696963375404737 
                  1                   1                   1                   1 
  0.700213649514998   0.738324705129217   0.763175748457544   0.768532924515416 
                  1                   1                   1                   1 
  0.782136300731067   0.821221195098089   0.881107726454215   0.918977371608218 
                  1                   1                   1                   1 
  0.943836210685299    1.06309983727636    1.10002537198388    1.12493091814311 
                  1                   1                   1                   1 
   1.16040261569495     1.1780869965732    1.20786780598317    1.35867955152904 
                  1                   1                   1                   1 
   1.43302370170104    1.46555486156289    1.51178116845085    1.58683345454085 
                  1                   1                   1                   1 
   1.59528080213779    1.98039989850586    2.17261167036215    2.40161776050478 
                  1                   1                   1                   1 
# Tables
set.seed(1)
a <- sample(1:5, 25, T)
a
 [1] 2 2 3 5 2 5 5 4 4 1 2 1 4 2 4 3 4 5 2 4 5 2 4 1 2
table(a)
a
1 2 3 4 5 
3 8 2 7 5 
prop.table(table(a)) # to obtain percentages
a
   1    2    3    4    5 
0.12 0.32 0.08 0.28 0.20 
prop.table(table(a)) *100
a
 1  2  3  4  5 
12 32  8 28 20 
cbind(table(a), prop.table(table(a))*100)
  [,1] [,2]
1    3   12
2    8   32
3    2    8
4    7   28
5    5   20
# multi-variate
b <- rep(c(1, 2), length = 25)
table(a, b)
   b
a   1 2
  1 0 3
  2 5 3
  3 1 1
  4 5 2
  5 2 3
c <- rep(c(3, 4, 5), length = 25)
table(a, b, c)
, , c = 3

   b
a   1 2
  1 0 1
  2 3 1
  3 0 1
  4 1 0
  5 1 1

, , c = 4

   b
a   1 2
  1 0 0
  2 2 2
  3 0 0
  4 2 2
  5 0 0

, , c = 5

   b
a   1 2
  1 0 2
  2 0 0
  3 1 0
  4 2 0
  5 1 2
ftable(a, c, c)
    c 3 4 5
a c        
1 3   1 0 0
  4   0 0 0
  5   0 0 2
2 3   4 0 0
  4   0 4 0
  5   0 0 0
3 3   1 0 0
  4   0 0 0
  5   0 0 1
4 3   1 0 0
  4   0 4 0
  5   0 0 2
5 3   2 0 0
  4   0 0 0
  5   0 0 3
xtabs(~a + b)
   b
a   1 2
  1 0 3
  2 5 3
  3 1 1
  4 5 2
  5 2 3
x <- table(a, b)
addmargins(x)
     b
a      1  2 Sum
  1    0  3   3
  2    5  3   8
  3    1  1   2
  4    5  2   7
  5    2  3   5
  Sum 13 12  25
prop.table(table(a, b), 1) # proportions by rows
   b
a           1         2
  1 0.0000000 1.0000000
  2 0.6250000 0.3750000
  3 0.5000000 0.5000000
  4 0.7142857 0.2857143
  5 0.4000000 0.6000000
prop.table(table(a, b), 2)
   b
a            1          2
  1 0.00000000 0.25000000
  2 0.38461538 0.25000000
  3 0.07692308 0.08333333
  4 0.38461538 0.16666667
  5 0.15384615 0.25000000
# Correlations
set.seed(1)
n <- 1000
x1 <- rnorm(n, -1, 10)
x2 <- rnorm(n, 3, 2)
y <- 5 * x1 + x2 + rnorm(n, 1, 2)
cor(x1, x2)
[1] 0.006401211
cor.test(x1, x2)

    Pearson's product-moment correlation

data:  x1 and x2
t = 0.20223, df = 998, p-value = 0.8398
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.05561394  0.06836716
sample estimates:
        cor 
0.006401211 
cor(cbind(x1, x2, y))
            x1          x2          y
x1 1.000000000 0.006401211 0.99837564
x2 0.006401211 1.000000000 0.04731214
y  0.998375645 0.047312138 1.00000000
a <- rnorm(n)
b <- a^2 + rnorm(n)
plot(b~a)

plot(b ~ a, col = "gray")
curve((x), col = "red", add = TRUE)
curve((x^2), col = "blue", add = TRUE)

cor(a, b)
[1] -0.01712362
cor(a^2, b)
[1] 0.8430192
plot(b~I(a^2), col = "orange")
abline(lm(b~I(a^2)), col = "red")

layout(matrix(1:2, nrow = 1))
plot(b ~ a, col = "gray")
curve((x^2), col = "blue", add = TRUE)
plot(b ~ I(a^2), col = "gray")
curve((x), col = "blue", add = TRUE)

# Rounding
height <- c(167, 164, 172, 158, 181, 179)
mean(height)
[1] 170.1667
signif(mean(height), 4)
[1] 170.2
round(mean(height), 1)
[1] 170.2
round(mean(height), -2)
[1] 200
options(digits = 5)
sd(height)
[1] 8.8863
options(digits = 2)
sd(height)
[1] 8.9
options(scipen = -10)
10000000
[1] 1e+07
# sprintf
sprintf("%.3f", pi)
[1] "3.142"
sprintf("%05.1f", pi)
[1] "003.1"
# Plots as data summary
set.seed(1)
a <- rnorm(30)
hist(a, col = "gray20", border = "lightgray")

density(a)

Call:
    density.default(x = a)

Data: a (30 obs.);  Bandwidth 'bw' = 0.3891

       x                  y          
 Min.   :-3.4e+00   Min.   :0.0e+00  
 1st Qu.:-1.8e+00   1st Qu.:3.0e-02  
 Median :-3.0e-01   Median :9.0e-02  
 Mean   :-3.0e-01   Mean   :1.6e-01  
 3rd Qu.: 1.2e+00   3rd Qu.:2.9e-01  
 Max.   : 2.8e+00   Max.   :4.6e-01  
plot(density(a))

hist(a, freq = FALSE, col = "gray20", border = "lightgray")
lines(density(a), col = "red", lwd = 2)

b <- c(3, 4.5, 5, 8, 3, 6)
barplot(b, names.arg = letters[1:length(b)], horiz = F)

d <- rbind(c(2, 4, 1), c(6, 1, 3))
d
      [,1]  [,2]  [,3]
[1,] 2e+00 4e+00 1e+00
[2,] 6e+00 1e+00 3e+00
barplot(d, names.arg = letters[1:3])

barplot(d, beside = T)

layout(matrix(1:2, nrow = 1))
barplot(b, names.arg = letters[1:6], horiz = TRUE, las = 2)
dotchart(b, labels = letters[1:6], xlim = c(0, 8))

boxplot(a)

e <- rnorm(100, 1, 1)
f <- rnorm(100, 2, 4)
boxplot(e, f)

g1 <- c(e, f)
g2 <- rep(c(1, 2), each = 100)
boxplot(g1 ~ g2)

# Scatterplot
x1 <- rnorm(1000)
x2 <- rnorm(1000)
x3 <- x1 + x2
x4 <- x1 + x3
plot(x1, x2)

plot(x2~x1)

layout(matrix(1:3, nrow = 1))
plot(x1, x2)
plot(x1, x3)
plot(x1, x4)

pairs(~x1 + x2 + x3 + x4)

colors()[1:10]
 [1] "white"         "aliceblue"     "antiquewhite"  "antiquewhite1" "antiquewhite2"
 [6] "antiquewhite3" "antiquewhite4" "aquamarine"    "aquamarine1"   "aquamarine2"  
length(colors())
[1] 657
colors()[600]
[1] "slategray1"
set.seed(100)
z <- sample(1:4, 100, TRUE)
x <- rnorm(100)
y <- rnorm(100)
plot(x, y, pch = 15, col = c("red", "blue"))

c("red", "blue", "green", "orange")[z]
  [1] "blue"   "blue"   "green"  "red"    "blue"   "blue"   "orange" "blue"   "green" 
 [10] "red"    "green"  "orange" "blue"   "blue"   "orange" "green"  "red"    "blue"  
 [19] "blue"   "green"  "green"  "green"  "green"  "green"  "blue"   "red"    "orange"
 [28] "orange" "green"  "blue"   "blue"   "orange" "blue"   "orange" "green"  "orange"
 [37] "red"    "green"  "orange" "red"    "blue"   "orange" "orange" "orange" "green" 
 [46] "blue"   "orange" "orange" "red"    "blue"   "blue"   "red"    "red"    "blue"  
 [55] "green"  "blue"   "red"    "red"    "green"  "red"    "blue"   "green"  "orange"
 [64] "green"  "blue"   "blue"   "blue"   "blue"   "red"    "green"  "blue"   "blue"  
 [73] "green"  "orange" "green"  "green"  "orange" "orange" "orange" "red"    "blue"  
 [82] "green"  "orange" "orange" "red"    "green"  "green"  "red"    "blue"   "green" 
 [91] "orange" "red"    "blue"   "blue"   "orange" "blue"   "green"  "red"    "red"   
[100] "orange"
plot(x, y, pch = 15, col = c("red", "blue", "green", "orange")[z]) #indexing colors on z groups

# Analysis of variance (ANOVA)
set.seed(100)
tr <- rep(1:4, each = 30)
y <- numeric(length = 120)
y[tr == 1] <- rnorm(30, 5, 1)
y[tr == 2] <- rnorm(30, 4, 2)
y[tr == 3] <- rnorm(30, 4, 5)
y[tr == 4] <- rnorm(30, 1, 2)
aov(y~tr)
Call:
   aov(formula = y ~ tr)

Terms:
                     tr Residuals
Sum of Squares  2.5e+02   1.2e+03
Deg. of Freedom 1.0e+00   1.2e+02

Residual standard error: 3.2e+00
Estimated effects may be unbalanced
summary(aov(y ~ factor(tr)))
                  Df   Sum Sq Mean Sq  F value  Pr(>F)    
factor(tr)  3.00e+00 2.97e+02 9.9e+01 9.98e+00 6.6e-06 ***
Residuals   1.16e+02 1.15e+03 9.9e+00                     
---
Signif. codes:  0e+00 ‘***’ 1e-03 ‘**’ 1e-02 ‘*’ 5e-02 ‘.’ 1e-01 ‘ ’ 1e+00
oneway.test(y ~ tr)

    One-way analysis of means (not assuming equal variances)

data:  y and tr
F = 4e+01, num df = 3e+00, denom df = 5e+01, p-value = 1e-13
oneway.test(y ~ factor(tr), var.equal = TRUE)

    One-way analysis of means

data:  y and factor(tr)
F = 1e+01, num df = 3e+00, denom df = 1e+02, p-value = 7e-06
by(y, tr, FUN = mean)
tr: 1
[1] 5e+00
------------------------------------------------------------------------------------ 
tr: 2
[1] 4.2e+00
------------------------------------------------------------------------------------ 
tr: 3
[1] 3.8e+00
------------------------------------------------------------------------------------ 
tr: 4
[1] 8.5e-01
tapply(y, tr, FUN = mean) # same thing
      1       2       3       4 
5.0e+00 4.2e+00 3.8e+00 8.5e-01 
# Distributions
options(scipen = F)
options(digits = 5)
dnorm(0)  # density
[1] 0.39894
dnorm(0, mean=-1)
[1] 0.24197
pnorm(0)  # cumulative
[1] 0.5
pnorm(1.65) # 95% normal confidence interval
[1] 0.95053
pnorm(1.96)
[1] 0.975
pnorm(1.96) - pnorm(-1.96)
[1] 0.95
qnorm(c(0.025, 0.975))  # quantile
[1] -1.96  1.96
pnorm(qnorm(c(0.025, 0.975)))
[1] 0.025 0.975
# other distribution
dbinom(0, 1, 0.5)
[1] 0.5
pbinom(0, 1, 0.5)
[1] 0.5
qbinom(.95, 1, 0.5)
[1] 1
# Formulae
myformula <- ~x
class(myformula)
[1] "formula"
# interactions
y ~ x1 * x2
y ~ x1 * x2
# As strings
("y ~ x") == (y ~ x)
[1] TRUE
as.formula("y~x")
y ~ x
as.character(y ~ x)
[1] "~" "y" "x"
terms(y ~ x1 + x2)
y ~ x1 + x2
attr(,"variables")
list(y, x1, x2)
attr(,"factors")
   x1 x2
y   0  0
x1  1  0
x2  0  1
attr(,"term.labels")
[1] "x1" "x2"
attr(,"order")
[1] 1 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
update(y ~ x, ~. + x2)
y ~ x + x2
update(y ~ x, z ~ .)
z ~ x
# Bivariate Regression
set.seed(1)
bin <- rbinom(1000, 1, 0.5)
out <- 2 * bin + rnorm(1000)
by(out, bin, mean)
bin: 0
[1] -0.015881
--------------------------------------------------------------------- 
bin: 1
[1] 1.9662
t.test(out ~ bin)

    Welch Two Sample t-test

data:  out by bin
t = -30.3, df = 993, p-value <2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -2.1105 -1.8537
sample estimates:
mean in group 0 mean in group 1 
      -0.015881        1.966237 
lm(out ~ bin)

Call:
lm(formula = out ~ bin)

Coefficients:
(Intercept)          bin  
    -0.0159       1.9821  
summary(lm(out~bin))$coef
             Estimate Std. Error  t value    Pr(>|t|)
(Intercept) -0.015881   0.045341 -0.35027  7.2621e-01
bin          1.982119   0.065444 30.28724 1.9487e-143
plot(out ~ bin, col = "gray")
points(0:1, by(out, bin, mean), col = "blue", bg = "blue", pch = 23)
abline(coef(lm(out ~ bin)), col = "blue")

set.seed(1)
x <- runif(1000, 0, 10)
y <- 3 * x + rnorm(1000, 0, 5)
# glm plots
set.seed(1)
n <- 100
x <- runif(n, 0, 1)
y <- rbinom(n, 1, x)
plot(y ~ x, col = NULL, bg = rgb(0, 0, 0, 0.5), pch = 21) # bg: background color
abline(lm(y ~ x), lwd = 2) # lwd: line width (default: 1)

m1 <- glm(y ~ x, family = binomial(link = "logit"))
newdf <- data.frame(x = seq(0, 1, length.out = 100))
newdf
newdf$pout_logit <- predict(m1, newdf, se.fit = TRUE, type = "response")$fit
newdf[95:100,]
# build confidence intervals from standard error
newdf$pse_logit <- predict(m1, newdf, se.fit = TRUE, type = "response")$se.fit
newdf$plower_logit <- newdf$pout_logit - (1.96 * newdf$pse_logit)  # 95% CI lower bound
newdf$pupper_logit <- newdf$pout_logit + (1.96 * newdf$pse_logit)  # 95% CI upper bound
# qnorm(c(0.025, 0.975)) = (-1.96, +1.96)
newdf[1:10,c(1,2,3,5,4)]
Error: object 'newdf' not found
# now plot predicted values
with(newdf, plot(pout_logit ~ x, type = "l", lwd = 2))
with(newdf, lines(pupper_logit ~ x, type = "l", lty = 2))
with(newdf, lines(plower_logit ~ x, type = "l", lty = 2))

head(mpg)
#g <- ggplot(data = mpg, aes(x = displ, y = hwy))
ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point()

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + geom_smooth(method = "lm") # with regression

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(size = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(shape = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(alpha = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(. ~ cyl)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(drv ~ .)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(drv ~ cyl)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_wrap( ~ class)

ggplot(data = mpg, aes(x = displ, y = hwy)) +  geom_point() + geom_smooth()

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + geom_smooth(se = FALSE)  # Turn off confidence band

ggplot(data = mpg, aes(x = class, y = hwy)) + geom_boxplot() # scatterplot

ggplot(data = mpg, aes(x = reorder(class, hwy), y = hwy)) + geom_boxplot() # reorder (mean)

ggplot(data = mpg, aes(x = reorder(class, hwy, median), y = hwy)) + geom_boxplot() # reorder by median

# jitter
ggplot(data = mpg, aes(x = cty, y = hwy)) + geom_point()

ggplot(data = mpg, aes(x = cty, y = hwy)) + geom_point(position = "jitter")

ggplot(data = mpg, aes(x = cty, y = hwy)) + geom_jitter() # identical option

names(diamonds) # part of ggplot2 package
 [1] "carat"   "cut"     "color"   "clarity" "depth"   "table"   "price"   "x"       "y"       "z"      
ggplot(data = diamonds,aes(x =cut)) + geom_bar(aes(fill =cut)) # fill: color inside of bars

ggplot(data = diamonds, aes(x =cut)) + geom_bar(aes(color =cut)) # color: line around the bars

ggplot(data = diamonds, aes(x = color)) + geom_bar(aes(fill = cut), position = "dodge")

str(diamonds)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   53940 obs. of  10 variables:
 $ carat  : num  0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
 $ cut    : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...
 $ color  : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
 $ clarity: Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
 $ depth  : num  61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
 $ table  : num  55 61 65 58 58 57 57 55 61 61 ...
 $ price  : int  326 326 327 334 335 336 336 337 337 338 ...
 $ x      : num  3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
 $ y      : num  3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
 $ z      : num  2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...
ggplot(data = diamonds, aes(x = carat)) + geom_histogram(binwidth = 1)

ggplot(data = diamonds, aes(x = carat)) + geom_histogram(binwidth = 0.01)

ggplot(data = diamonds, aes(x = carat)) + geom_histogram() #stat_bin: binwidth defaulted to range/30.

zoom <- coord_cartesian(xlim = c(55, 70))
ggplot(data = diamonds, aes(x = depth)) + geom_histogram(binwidth = 0.2) + zoom

ggplot(data = diamonds, aes(x = depth)) + geom_histogram(aes(fill = cut), binwidth = 0.1) + zoom

ggplot(data = diamonds, aes(x = depth)) + geom_density(aes(fill = cut)) + zoom

ggplot(data = diamonds, aes(x = depth)) + geom_density(aes(color = cut, fill = cut, alpha=0.1)) + zoom

ggplot(data = diamonds, aes(x = price)) +geom_histogram(aes(fill = cut), binwidth = 100)

ggplot(data = diamonds, aes(x = price)) + geom_density(aes(color= cut))

# Visualization of big data
ggplot(data = diamonds, aes(x = carat, y = price)) + geom_point(aes(color = cut)) # not helpful

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_bin2d()

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_density2d()

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_point() + geom_density2d()

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_smooth(aes(group = cut))
ggplot(data = diamonds, aes(x = carat, y = price)) + geom_smooth(aes(color = cut), method = "loess", se=F)

# ggsave("my-plot.pdf") # PDF more crisp
# ggsave("my-plot.png")
# ggsave("my-plot.pdf", width = 6, height = 6)
# ggsave("my-plot.png", width = 6, height = 6)
head(births, 3)

head(bnames, 3)
# Simple plot
Quentin <- bnames[bnames$name == "Letitia", ] 
qplot(year, prop, data = Quentin, geom = "line")

# Interactions
michaels <- bnames[bnames$name == "Quentin" | bnames$name == "Alexis" | bnames$name == "Gina", ]
qplot(year, prop, data = michaels, geom = "line", color = interaction(sex, name))

# dplyr
library(dplyr)
bnames = tbl_df(bnames) 
births = tbl_df(births) 
class(bnames)
[1] "tbl_df"     "tbl"        "data.frame"
# filter
vivian = filter(bnames, name == "Vivian")
filter(bnames, sex == "girl" & (year == 1900 | year == 2000))
# Select columns
select(bnames, starts_with("sound"))
# Rename column
rename(iris, petal_length = Petal.Length)
# sort
arrange(bnames, desc(prop))[3,]
# year where Quentin was most popular
arrange(filter(bnames, name == "Quentin"), desc(prop))[1,]
# add columns
mutate(births, double = 2 * births)
# transmute to delete old columns
# Summarize
summarise(vivian,min = min(prop),mean = mean(prop), max = max(prop), number = n(), number_distinct = n_distinct(prop))
bnames2 = left_join(bnames, births, by = c("year","sex"))
bnames2 = mutate(bnames2, n = round(prop * births))
# Group
by_name = group_by(bnames2, name)
by_name
totals = summarise(by_name, total = sum(n)) 
totals
bnames2
name_sex = group_by(bnames2, name, year) 
totals2 = summarise(name_sex, total = sum(n)) 
head(totals2)
arrange(bnames2, name)[1:3,]
ungroup(by_name)
# other example
year_sex = group_by(bnames2, year, sex) 
ytotals = summarise(year_sex, births = sum(n)) 
ytotals
# ISLR 
# Chap 6 Ridge regression / Lasso
# Ex 8
set.seed(1)
X = rnorm(100)
eps = rnorm(100)
beta0 = 3
beta1 = 2
beta2 = -3
beta3 = 0.3
Y = beta0 + beta1 * X + beta2 * X^2 + beta3 * X^3 + eps
# Use regsubsets to select best model having polynomial of XX of degree 10
library(leaps)
data.full = data.frame(y = Y, x = X)
mod.full = regsubsets(y ~ poly(x, 10, raw = T), data = data.full, nvmax = 10)
mod.summary = summary(mod.full)
# Find the model size for best cp, BIC and adjr2
which.min(mod.summary$cp)
[1] 3
which.min(mod.summary$bic)
[1] 3
which.max(mod.summary$adjr2)
[1] 3
# Plot cp, BIC and adjr2
plot(mod.summary$cp, xlab = "Subset Size", ylab = "Cp", pch = 20, type = "l")
points(3, mod.summary$cp[3], pch = 4, col = "red", lwd = 7)

plot(mod.summary$bic, xlab = "Subset Size", ylab = "BIC", pch = 20, type = "l")
points(3, mod.summary$bic[3], pch = 4, col = "red", lwd = 7)

plot(mod.summary$adjr2, xlab = "Subset Size", ylab = "Adjusted R2", pch = 20, 
    type = "l")
points(3, mod.summary$adjr2[3], pch = 4, col = "red", lwd = 7)

# Coefs found by regression (replaces x^3 by x^7)
coefficients(mod.full, id = 3)
          (Intercept) poly(x, 10, raw = T)1 poly(x, 10, raw = T)2 poly(x, 10, raw = T)7 
           3.07627412            2.35623596           -3.16514887            0.01046843 
# Now lasso
library(glmnet)
xmat = model.matrix(y ~ poly(x, 10, raw = T), data = data.full)[, -1] # remove intercept (first column)
mod.lasso = cv.glmnet(xmat, Y, alpha = 1) # cv.glmnet: cross validation to select best lambda
best.lambda = mod.lasso$lambda.min
best.lambda
[1] 0.03991416
plot(mod.lasso)

# Next fit the model on entire data using best lambda
best.model = glmnet(xmat, Y, alpha = 1)
predict(best.model, s = best.lambda, type = "coefficients")
11 x 1 sparse Matrix of class "dgCMatrix"
                                   1
(Intercept)             3.0398151056
poly(x, 10, raw = T)1   2.2303371338
poly(x, 10, raw = T)2  -3.1033192679
poly(x, 10, raw = T)3   .           
poly(x, 10, raw = T)4   .           
poly(x, 10, raw = T)5   0.0498410763
poly(x, 10, raw = T)6   .           
poly(x, 10, raw = T)7   0.0008068431
poly(x, 10, raw = T)8   .           
poly(x, 10, raw = T)9   .           
poly(x, 10, raw = T)10  .           
#Lasso also picks X^5 over X^3. It also picks X^7 with negligible coefficient.
# ISLR 
# Chap 7 Non-linear Modeling - Splines, GAM
library(ISLR)
attach(Wage)
The following objects are masked from Wage (pos = 3):

    age, education, health, health_ins, jobclass, logwage, maritl, race, region, wage, year
fit=lm(wage~poly(age,4,raw=T),data=Wage)  # raw=T to obtain coefficients of poly directly
coef(summary(fit)) # below we see small coef (and p-value) for order 3 & 4
                            Estimate   Std. Error   t value     Pr(>|t|)
(Intercept)            -1.841542e+02 6.004038e+01 -3.067172 0.0021802539
poly(age, 4, raw = T)1  2.124552e+01 5.886748e+00  3.609042 0.0003123618
poly(age, 4, raw = T)2 -5.638593e-01 2.061083e-01 -2.735743 0.0062606446
poly(age, 4, raw = T)3  6.810688e-03 3.065931e-03  2.221409 0.0263977518
poly(age, 4, raw = T)4 -3.203830e-05 1.641359e-05 -1.951938 0.0510386498
# Equivalent expressions
fit2a=lm(wage~age+I(age^2)+I(age^3)+I(age^4),data=Wage)
coef(fit2a)
  (Intercept)           age      I(age^2)      I(age^3)      I(age^4) 
-1.841542e+02  2.124552e+01 -5.638593e-01  6.810688e-03 -3.203830e-05 
fit2b=lm(wage~cbind(age,age^2,age^3,age^4),data=Wage) # cbind in formulas <-> I() wrapper
coef(fit2b)
                       (Intercept) cbind(age, age^2, age^3, age^4)age    cbind(age, age^2, age^3, age^4) 
                     -1.841542e+02                       2.124552e+01                      -5.638593e-01 
   cbind(age, age^2, age^3, age^4)    cbind(age, age^2, age^3, age^4) 
                      6.810688e-03                      -3.203830e-05 
# Prediction
agelims=range(age)
age.grid=seq(from=agelims[1],to=agelims[2])
preds=predict(fit,newdata=list(age=age.grid),se=TRUE) # creates list of ages where we want prediction (see below)
se.bands=cbind(preds$fit+2*preds$se.fit,preds$fit-2*preds$se.fit)
par(mfrow=c(1,2),mar=c(4.5,4.5,1,1),oma=c(0,0,4,0)) # mar/ oma: margins of plot
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
title("Degree-4 Polynomial",outer=T)
lines(age.grid,preds$fit,lwd=2,col="blue")
matlines(age.grid,se.bands,lwd=1,col="blue",lty=3)

age.grid=seq(from=agelims[1],to=agelims[2]) 
list(age=age.grid) # $age list
$age
 [1] 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
[34] 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
predict(fit,list(age=c(70))) # test for 1 age - still need to create an $age list
       1 
106.9478 
preds
$fit
        1         2         3         4         5         6         7         8         9        10 
 51.93145  58.49674  64.57188  70.18273  75.35440  80.11122  84.47676  88.47380  92.12437  95.44973 
       11        12        13        14        15        16        17        18        19        20 
 98.47038 101.20601 103.67560 105.89731 107.88856 109.66599 111.24548 112.64214 113.87029 114.94351 
       21        22        23        24        25        26        27        28        29        30 
115.87459 116.67557 117.35770 117.93148 118.40662 118.79210 119.09608 119.32598 119.48846 119.58939 
       31        32        33        34        35        36        37        38        39        40 
119.63388 119.62627 119.57013 119.46827 119.32271 119.13473 118.90481 118.63269 118.31733 117.95691 
       41        42        43        44        45        46        47        48        49        50 
117.54885 117.08980 116.57566 116.00152 115.36174 114.64988 113.85877 112.98043 112.00613 110.92638 
       51        52        53        54        55        56        57        58        59        60 
109.73090 108.40865 106.94784 105.33588 103.55943 101.60437  99.45583  97.09815  94.51491  91.68893 
       61        62        63 
 88.60223  85.23611  81.57105 

$se.fit
        1         2         3         4         5         6         7         8         9        10 
 5.298268  4.370763  3.592101  2.955813  2.455588  2.083260  1.825676  1.662329  1.566864  1.512533 
       11        12        13        14        15        16        17        18        19        20 
 1.477572  1.447355  1.413769  1.373549  1.326690  1.275230  1.222382  1.171865  1.127324  1.091786 
       21        22        23        24        25        26        27        28        29        30 
 1.067171  1.053981  1.051263  1.056899  1.068100  1.081939  1.095809  1.107729  1.116537  1.121992 
       31        32        33        34        35        36        37        38        39        40 
 1.124827  1.126747  1.130363  1.139015  1.156439  1.186260  1.231415  1.293640  1.373218  1.469069 
       41        42        43        44        45        46        47        48        49        50 
 1.579082  1.700568  1.830715  1.966988  2.107496  2.251362  2.399124  2.553187  2.718307  2.902040 
       51        52        53        54        55        56        57        58        59        60 
 3.115018  3.370894  3.685814  4.077417  4.563601  5.161438  5.886530  6.752911  7.773327  8.959688 
       61        62        63 
10.323512 11.876272 13.629642 

$df
[1] 2995

$residual.scale
[1] 39.91479
# Use of ANOVA
fit.1=lm(wage~age,data=Wage)
fit.2=lm(wage~poly(age,2),data=Wage)
fit.3=lm(wage~poly(age,3),data=Wage)
fit.4=lm(wage~poly(age,4),data=Wage)
fit.5=lm(wage~poly(age,5),data=Wage)
anova(fit.1,fit.2,fit.3,fit.4,fit.5) # Analysis of variance
Analysis of Variance Table

Model 1: wage ~ age
Model 2: wage ~ poly(age, 2)
Model 3: wage ~ poly(age, 3)
Model 4: wage ~ poly(age, 4)
Model 5: wage ~ poly(age, 5)
  Res.Df     RSS Df Sum of Sq        F    Pr(>F)    
1   2998 5022216                                    
2   2997 4793430  1    228786 143.5931 < 2.2e-16 ***
3   2996 4777674  1     15756   9.8888  0.001679 ** 
4   2995 4771604  1      6070   3.8098  0.051046 .  
5   2994 4770322  1      1283   0.8050  0.369682    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Or obtain these p-values by exploiting the fact that poly() creates orthogonal polynomials:
coef(summary(fit.5)) # and check P-values for coef of each order
                Estimate Std. Error     t value     Pr(>|t|)
(Intercept)    111.70361  0.7287647 153.2780243 0.000000e+00
poly(age, 5)1  447.06785 39.9160847  11.2001930 1.491111e-28
poly(age, 5)2 -478.31581 39.9160847 -11.9830341 2.367734e-32
poly(age, 5)3  125.52169 39.9160847   3.1446392 1.679213e-03
poly(age, 5)4  -77.91118 39.9160847  -1.9518743 5.104623e-02
poly(age, 5)5  -35.81289 39.9160847  -0.8972045 3.696820e-01
# Generalized linear function
fit=glm(I(wage>250)~poly(age,4),data=Wage,family=binomial) # plus create binary response on the fly with I()
preds=predict(fit,newdata=list(age=age.grid),se=T) # default: type="link" = predictions for logit
# other option type="response" (probability vs odds)
# Step function
table(cut(age,4))

(17.9,33.5]   (33.5,49]   (49,64.5] (64.5,80.1] 
        750        1399         779          72 
fit=lm(wage~cut(age,4),data=Wage) # cut() returns an ordered categorical variable. Breaks= can be used 
coef(summary(fit))
                        Estimate Std. Error   t value     Pr(>|t|)
(Intercept)            94.158392   1.476069 63.789970 0.000000e+00
cut(age, 4)(33.5,49]   24.053491   1.829431 13.148074 1.982315e-38
cut(age, 4)(49,64.5]   23.664559   2.067958 11.443444 1.040750e-29
cut(age, 4)(64.5,80.1]  7.640592   4.987424  1.531972 1.256350e-01
fit.1=lm(wage~education+age,data=Wage)
fit.2=lm(wage~education+poly(age,2),data=Wage)
fit.3=lm(wage~education+poly(age,3),data=Wage)
anova(fit.1,fit.2,fit.3)
Analysis of Variance Table

Model 1: wage ~ education + age
Model 2: wage ~ education + poly(age, 2)
Model 3: wage ~ education + poly(age, 3)
  Res.Df     RSS Df Sum of Sq        F Pr(>F)    
1   2994 3867992                                 
2   2993 3725395  1    142597 114.6969 <2e-16 ***
3   2992 3719809  1      5587   4.4936 0.0341 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Splines
library(splines)
fit=lm(wage~bs(age,knots=c(25,40,60)),data=Wage) # 3 knots --> 7 degrees of freedom --> 1 intercept + 6 basis functions
pred=predict(fit,newdata=list(age=age.grid),se=T)
plot(age,wage,col="gray")
lines(age.grid,pred$fit,lwd=2)
lines(age.grid,pred$fit+2*pred$se,lty="dashed")
lines(age.grid,pred$fit-2*pred$se,lty="dashed")

pred=predict(fit,se=T)
plot(age,wage,col="gray")
lines(age,pred$fit,lwd=2) # too many points: that's why we changed age into a list of all unique ages

# That works too - just need to have same length of data in predict() and lines()
age.grid2=sort(unique(age))
pred2=predict(fit,newdata=list(age=age.grid2),se=T)
plot(age,wage,col="gray")
lines(age.grid2,pred2$fit,lwd=2)

str(pred2)
List of 4
 $ fit           : Named num [1:61] 60.5 62.7 65.8 69.6 73.8 ...
  ..- attr(*, "names")= chr [1:61] "1" "2" "3" "4" ...
 $ se.fit        : Named num [1:61] 9.46 5.63 3.78 3.33 3.22 ...
  ..- attr(*, "names")= chr [1:61] "1" "2" "3" "4" ...
 $ df            : int 2993
 $ residual.scale: num 39.9
dim(bs(age,knots=c(25,40,60), degree = 3)) # bs generates matrix with 6 basis functions (see below)
[1] 3000    6
dim(bs(age,df=6)) # df option to produce a spline with knots at uniform quantiles of the data
[1] 3000    6
attr(bs(age,df=6),"knots")
  25%   50%   75% 
33.75 42.00 51.00 
# Natural spline: ns()
fit2=lm(wage~ns(age,df=4),data=Wage)
pred2=predict(fit2,newdata=data.frame(age=age.grid),se=T)
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
lines(age.grid, pred2$fit,col="red",lwd=2)

# Smooth spline
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
title("Smoothing Spline")
fit=smooth.spline(age,wage,df=16) # forces 16 degrees of freedom
fit2=smooth.spline(age,wage,cv=TRUE) # choose value of lambda by cross validation --> 6.8 degrees of freedom
cross-validation with non-unique 'x' values seems doubtful
fit2$df
[1] 6.794596
lines(fit,col="red",lwd=2)
lines(fit2,col="blue",lwd=2)
legend("topright",legend=c("16 DF","6.8 DF"),col=c("red","blue"),lty=1,lwd=2,cex=.8)

# Local regresssion (Loess)
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
title("Local Regression")
fit=loess(wage~age,span=.2,data=Wage) # span=0.2 means use 20% of observations
fit2=loess(wage~age,span=.5,data=Wage)
lines(age.grid,predict(fit,data.frame(age=age.grid)),col="red",lwd=2)
lines(age.grid,predict(fit2,data.frame(age=age.grid)),col="blue",lwd=2)
legend("topright",legend=c("Span=0.2","Span=0.5"),col=c("red","blue"),lty=1,lwd=2,cex=.8)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKYGBge3J9CkEgPC0gMTo1CkIgPC0gMTE6MTUKbmFtZXMoQSkgPC0gQQpuYW1lcyhCKSA8LSBCCmBgYAoKYGBge3J9CkEKQgpgYGAKCmBgYHtyfQpWaWV3KGFuc2NvbWJlKQpsbSh5M354MywgZGF0YSA9IGFuc2NvbWJlKQpgYGAKCmBgYHtyfQojIy0tIG5vdyBzb21lICJtYWdpYyIgdG8gZG8gdGhlIDQgcmVncmVzc2lvbnMgaW4gYSBsb29wOgpmZiA8LSB5IH4geAptb2RzIDwtIHNldE5hbWVzKGFzLmxpc3QoMTo0KSwgcGFzdGUwKCJsbSIsIDE6NCkpCmZvcihpIGluIDE6NCkgewogIGZmWzI6M10gPC0gbGFwcGx5KHBhc3RlMChjKCJ5IiwieCIpLCBpKSwgYXMubmFtZSkKICAjIyBvciAgIGZmW1syXV0gPC0gYXMubmFtZShwYXN0ZTAoInkiLCBpKSkKICAjIyAgICAgIGZmW1szXV0gPC0gYXMubmFtZShwYXN0ZTAoIngiLCBpKSkKICBtb2RzW1tpXV0gPC0gbG1pIDwtIGxtKGZmLCBkYXRhID0gYW5zY29tYmUpCiAgcHJpbnQoYW5vdmEobG1pKSkKfQpgYGAKCmBgYHtyfQojIyBTZWUgaG93IGNsb3NlIHRoZXkgYXJlIChudW1lcmljYWxseSEpCnNhcHBseShtb2RzLCBjb2VmKQpsYXBwbHkobW9kcywgZnVuY3Rpb24oZm0pIGNvZWYoc3VtbWFyeShmbSkpKQpgYGAKCmBgYHtyfQojIEFnZ3JlZ2F0ZSBmdW5jdGlvbgojU3BsaXRzIHRoZSBkYXRhIGludG8gc3Vic2V0cywgY29tcHV0ZXMgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoLCBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGluIGEgY29udmVuaWVudCBmb3JtCnRlc3RERiA8LSBkYXRhLmZyYW1lKHYxID0gYygxLDMsNSw3LDgsMyw1LE5BLDQsNSw3LDkpLAogICAgICAgICAgICAgICAgICAgICB2MiA9IGMoMTEsMzMsNTUsNzcsODgsMzMsNTUsTkEsNDQsNTUsNzcsOTkpICkKYnkxIDwtIGMoInJlZCIsICJibHVlIiwgMSwgMiwgTkEsICJiaWciLCAxLCAyLCAicmVkIiwgMSwgTkEsIDEyKQpieTIgPC0gYygid2V0IiwgImRyeSIsIDk5LCA5NSwgTkEsICJkYW1wIiwgOTUsIDk5LCAicmVkIiwgOTksIE5BLCBOQSkKYWdncmVnYXRlKHggPSB0ZXN0REYsIGJ5ID0gbGlzdChieTEsIGJ5MiksIEZVTiA9ICJtZWFuIikKCiMgb3IgZnJvbSBUaXRhbmljIEthZ2cKI2FnZ3JlZ2F0ZShTdXJ2aXZlZCB+IEZhcmUyICsgUGNsYXNzICsgU2V4LCBkYXRhPXRyYWluLCBGVU49ZnVuY3Rpb24oeCkge3N1bSh4KS9sZW5ndGgoeCl9KQpgYGAKCmBgYHtyfQojIyBOb3csIGRvIHdoYXQgeW91IHNob3VsZCBoYXZlIGRvbmUgaW4gdGhlIGZpcnN0IHBsYWNlOiBQTE9UUwpvcCA8LSBwYXIobWZyb3cgPSBjKDIsIDIpLCBtYXIgPSAwLjErYyg0LDQsMSwxKSwgb21hID0gIGMoMCwgMCwgMiwgMCkpCmZvcihpIGluIDE6NCkgewogIGZmWzI6M10gPC0gbGFwcGx5KHBhc3RlMChjKCJ5IiwieCIpLCBpKSwgYXMubmFtZSkKICBwbG90KGZmLCBkYXRhID0gYW5zY29tYmUsIGNvbCA9ICJyZWQiLCBwY2ggPSAyMSwgYmcgPSAib3JhbmdlIiwgY2V4ID0gMS4yLAogICAgICAgeGxpbSA9IGMoMywgMTkpLCB5bGltID0gYygzLCAxMykpCiAgYWJsaW5lKG1vZHNbW2ldXSwgY29sID0gImJsdWUiKQp9Cm10ZXh0KCJBbnNjb21iZSdzIDQgUmVncmVzc2lvbiBkYXRhIHNldHMiLCBvdXRlciA9IFRSVUUsIGNleCA9IDEuNSkKcGFyKG9wKQpgYGAKCmBgYHtyfQp4ID0gYyhUUlVFLCBGQUxTRSwgRkFMU0UpCnR5cGVvZih4KSAgI2xvZ2ljYWwgKHZlY3RvcikKbW9kZSh4KQpzdG9yYWdlLm1vZGUoeCkKeSA9IDE6MTAKdHlwZW9mKHkpCm1vZGUoeSkKc3RvcmFnZS5tb2RlKHkpCmBgYAoKYGBge3J9CmRpbSh5KQpsZW5ndGgoeSkKYGBgCgpgYGB7cn0Kej0gbGlzdCgxLCBUUlVFLCAnc2FmcycpICN0cnlpbmcgdG8gZ2V0IGEgbGlzdAp0eXBlb2YoeikKY2xhc3MoeikKelszXQpsZW5ndGgoeikKZGltKHopCgpgYGAKCmBgYHtyfQpxdW90ZSh4K3kpCmFzLmxpc3QocXVvdGUoeCArIHkpKQpgYGAKCmBgYHtyfQoxZTNMICNjcmVhdGUgY29uc3RhbnQKMUwKYGBgCgpgYGB7cn0KY2F0KDErMikKJysnKDEsIDIpCmBgYAoKYGBge3J9CnggPC0gb3B0aW9ucygpCngkcHJvbXB0CmBgYAoKYGBge3J9Cm1hdGNoKE5BLCBOYU4pCm1hdGNoKE5BLCBOQSkKbWF0Y2goTmFOLCBOYU4pCmBgYAoKYGBge3J9CnggPSBhcnJheSgxOjgsIGMoMiw0KSkKeAppPTIKaj0zCnhbaV0KeFtpLCBqXQp4W1tpXV0KeFtbaSwgal1dCmBgYAoKYGBge3J9CnJvd25hbWVzKHgpPWMoImEiLCJiIikKeAp4ID0gYXMuZGF0YS5mcmFtZSh4KQp4CnhbImEiLF0KeFtdCmBgYAoKYGBge3J9CmkgPC0gbWF0cml4KDE6NCwgMiwgYnlyb3cgPSBUUlVFKQppCmBgYAoKYGBge3J9CmlbMixdCmlbMiwgLGRyb3A9RkFMU0VdICMga2VlcGluZyBkaW1lbnNpb24gMSAqIG4gd2hlbiBzZWxlY3Rpb24gYSByb3cKZGltKGlbMixdKQpkaW0oaVsyLCAsZHJvcD1GQUxTRV0pCmBgYAoKYGBge3J9CiMgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvZG9jL21hbnVhbHMvUi1pbnRyby5wZGYKCmhlbHAuc3RhcnQoKQpgYGAKCmBgYHtyfQp4IDwtIHJub3JtKDEwMDAwKQp5IDwtIHJub3JtKHgpCnBsb3QoeCwgeSkKaGlzdCh5KQpgYGAKCmBgYHtyfQpscygpCmBgYAoKYGBge3J9CnJtKGxpc3Q9bHMoKSkKbHMoKQpgYGAKCmBgYHtyfQp4IDwtIDE6MjAKdyA8LSAxICsgc3FydCh4KS8yCmR1bW15IDwtIGRhdGEuZnJhbWUoeD14LCB5PSB4ICsgcm5vcm0oeCkqdykKZHVtbXkgCmBgYAoKYGBge3J9CiMgNCBPcmRlcmVkIGFuZCB1bm9yZGVyZWQgZmFjdG9ycwpzdGF0ZSA8LSBjKCJ0YXMiLCAic2EiLCAicWxkIiwgIm5zdyIsICJuc3ciLCAibnQiLCAid2EiLCAid2EiLAoicWxkIiwgInZpYyIsICJuc3ciLCAidmljIiwgInFsZCIsICJxbGQiLCAic2EiLCAidGFzIiwKInNhIiwgIm50IiwgIndhIiwgInZpYyIsICJxbGQiLCAibnN3IiwgIm5zdyIsICJ3YSIsCiJzYSIsICJhY3QiLCAibnN3IiwgInZpYyIsICJ2aWMiLCAiYWN0IikKc3RhdGVmIDwtIGZhY3RvcihzdGF0ZSkKc3RhdGVmCmBgYAoKYGBge3J9CmxldmVscyhzdGF0ZWYpCmBgYAoKYGBge3J9CmluY29tZXMgPC0gYyg2MCwgNDksIDQwLCA2MSwgNjQsIDYwLCA1OSwgNTQsIDYyLCA2OSwgNzAsIDQyLCA1NiwKNjEsIDYxLCA2MSwgNTgsIDUxLCA0OCwgNjUsIDQ5LCA0OSwgNDEsIDQ4LCA1MiwgNDYsCjU5LCA0NiwgNTgsIDQzKQppbmNtZWFucyA8LSB0YXBwbHkoaW5jb21lcywgc3RhdGVmLCBtZWFuKQppbmNtZWFucwpgYGAKCmBgYHtyfQojIEFycmF5cwphIDwtIGFycmF5KDE6MzAsIGRpbT1jKDIsIDUsMykpCmEKYGBgCgpgYGB7cn0KeCA8LSBhcnJheSgxOjIwLCBkaW09Yyg0LDUpKSAjIEdlbmVyYXRlIGEgNCBieSA1IGFycmF5Lgp4CmkgPC0gYXJyYXkoYygxOjMsMzoxKSwgZGltPWMoMywyKSkKaQp4W2ldICNnZXQgdGhlIDMgZWxlbWVudHMgc2hvd24gYnkgaTogKDMsIDEpLCAoMiwgMikgYW5kICgxLCAzKQpgYGAKCmBgYHtyfQpoZWxwKCI8LSIpCmBgYAoKYGBge3J9CiMgQmFjayB0byBodHRwOi8vem9vbmVrMi5mcmVlLmZyL1VOSVgvNDhfUi8wMi5odG1sCnggPC0gcm5vcm0oMTApCngKc29ydCh4KQpvcmRlcih4KQp4W29yZGVyKHgpXQpgYGAKCmBgYHtyfQp4IDwtIHNhbXBsZSgxOjUsIDEwLCByZXBsYWNlPVQpCngKeFtvcmRlcih4KV0KdW5pcXVlKHgpCmBgYAoKYGBge3J9CnNlcSgwLDEwLCBsZW5ndGg9MTEpID09IHNlcSgwLDEwLCBieT0xKQpgYGAKCmBgYHtyfQojIFJlcApyZXAoMCwgMTApCnJlcCgxOjUsMykKcmVwKDE6NSwgZWFjaD0zKQpyZXAoMTo1LDIsZWFjaD0zKQpgYGAKCmBgYHtyfQojIEZhY3Rvcgp4IDwtIGZhY3Rvciggc2FtcGxlKGMoIlllcyIsICJObyIsICJQZXJoYXBzIiksIDUsIHJlcGxhY2U9VCkgKQp4CmBgYAoKYGBge3J9CiMgc3BlY2lmeSBsZXZlbHMKbCA8LSBjKCJZZXMiLCAiTm8iLCAiUGVyaGFwcyIpCnggPC0gZmFjdG9yKCBzYW1wbGUobCwgNSwgcmVwbGFjZT1UKSwgbGV2ZWxzPWwgKQp4CmBgYAoKYGBge3J9CnRhYmxlKHgpCmBgYAoKYGBge3J9CiMgZ2w6IEdlbmVyYXRlIEZhY3RvciBMZXZlbHMKZ2woMSw0KQpnbCgyLDQpCmdsKDIsMSw4KQpnbCgyLDEsOCwgbGFiZWxzPWMoVCxGKSkKYGBgCgpgYGB7cn0KeCA8LSBnbCgyLDQpCngKeSA8LSBnbCgyLDEsOCkKeQppbnRlcmFjdGlvbih4LHkpCmRhdGEuZnJhbWUoeCx5LCBpbnQ9aW50ZXJhY3Rpb24oeCx5KSkKYGBgCgpgYGB7cn0KIyBDYXJ0ZXNpYW4gcHJvZHVjdCAodG91dGVzIGxlcyBwb3NzaWJpbGl0ZXMpCnggPC0gYygiQSIsICJCIiwgIkMiKQp5IDwtIDE6Mgp6IDwtIGMoImEiLCAiYiIpCmV4cGFuZC5ncmlkKHgseSx6KQpgYGAKCmBgYHtyfQp4IDwtIGZhY3RvcihjKDMsNCw1LDEpKQphcy5udW1lcmljKCBsZXZlbHMoeCkpCmFzLm51bWVyaWMoIGxldmVscyh4KVsgeCBdICkgIyBwcm9wZXIgd2F5IHRvIGNvbnZlcnQgZnJvbSBmYWN0b3IgdG8gbnVtZXJpYwpgYGAKCmBgYHtyfQojIERhdGEgRnJhbWVzCm4gPC0gMTAKZGYgPC0gZGF0YS5mcmFtZSggeD1ybm9ybShuKSwgeT1zYW1wbGUoYyhULEYpLG4scmVwbGFjZT1UKSApCnN0cihkZikKYGBgCgpgYGB7cn0Kc3VtbWFyeShkZikKYGBgCgpgYGB7cn0KbmFtZXMoZGYpO2NhdChyb3duYW1lcyhkZikpCmBgYAoKYGBge3J9CiMgTWVyZ2UKbWVyZ2UoeCwgeSkgICAgICAgICAgICAgICAgICMgSU5ORVIgSk9JTgptZXJnZSh4LCB5LCBhbGwueCA9IFRSVUUpICAgIyBMRUZUIEpPSU4KbWVyZ2UoeCwgeSwgYWxsLnkgPSBUUlVFKSAgICMgUklHSFQgSk9JTgptZXJnZSh4LCB5LCBhbGwgICA9IFRSVUUpICAgIyBPVVRFUiBKT0lOCiNtZXJnZShhLCBiLCBieT1jKCJ5IiwgInoiKSkgIyBzcGVjaWZ5IHdoYXQgdG8gbWVyZ2Ugb24KYGBgCgpgYGB7cn0KIyBSZWdyZXNzaW9uCmRhdGEoY2FycykgCiNWaWV3KGNhcnMpCgojIFJlZ3Jlc3Npb24KbG0uZml0PWxtKCBkaXN0IH4gc3BlZWQsIGRhdGE9Y2FycywgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSkKbG0uZml0CiMgUG9seW5vbWlhbCByZWdyZXNzaW9uCmxtLmZpdDMgPSBsbSggZGlzdCB+IHBvbHkoc3BlZWQsMyksIGRhdGE9Y2FycykKCiNwbG90KGxtLmZpdCkKcGxvdChjYXJzJHNwZWVkLCBjYXJzJGRpc3QpCmFibGluZShsbS5maXQpCmBgYAoKYGBge3J9CiMgVHJlZXMKIyBpbnN0YWxsLnBhY2thZ2VzKCdyYXR0bGUnKQojIGluc3RhbGwucGFja2FnZXMoJ3JwYXJ0LnBsb3QnKQojIGluc3RhbGwucGFja2FnZXMoJ1JDb2xvckJyZXdlcicpCmxpYnJhcnkocmF0dGxlKQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCmBgYHtyfQojIGh0dHA6Ly90cmV2b3JzdGVwaGVucy5jb20va2FnZ2xlLXRpdGFuaWMtdHV0b3JpYWwvci1wYXJ0LTUtcmFuZG9tLWZvcmVzdHMvCiMgZml0IDwtIHJwYXJ0KFN1cnZpdmVkIH4gUGNsYXNzICsgU2V4ICsgQWdlICsgU2liU3AgKyBQYXJjaCArIEZhcmUgKyBFbWJhcmtlZCwKIyAgICAgICAgICAgICAgICBkYXRhPXRyYWluLAojICAgICAgICAgICAgICAgIG1ldGhvZD0iY2xhc3MiLCAKIyAgICAgICAgICAgICAgICBjb250cm9sPXJwYXJ0LmNvbnRyb2wobWluc3BsaXQ9MiwgY3A9MCkpCiMgZmFuY3lScGFydFBsb3QoZml0KQoKIyBQcmVkaWN0IGEgY29udGludW91cyB2YXJpYWJsZSAobWV0aG9kIGFub3ZhKQojIEFnZWZpdCA8LSBycGFydChBZ2UgfiBQY2xhc3MgKyBTZXggKyBTaWJTcCArIFBhcmNoICsgRmFyZSArIEVtYmFya2VkICsgVGl0bGUgKyBGYW1pbHlTaXplLAojICAgICAgICAgICAgICAgICAgIGRhdGE9Y29tYmlbIWlzLm5hKGNvbWJpJEFnZSksXSwgCiMgICAgICAgICAgICAgICAgICAgbWV0aG9kPSJhbm92YSIpCmBgYAoKYGBge3J9CiMgTGlzdHMKaCA8LSBsaXN0KCkKaFtbImZvbyJdXSA8LSAxCmhbWyJiYXIiXV0gPC0gYygiYSIsICJiIiwgImMiKQpoWyJiYXIiXSA9PSBoW1siYmFyIl1dICNoWyJiYXIiXSBpcyBhIGxpc3QgY29udGFpbmluZyB0aGUgdmVjdG9yCmBgYAoKYGBge3J9CiMgRGVsZXRlCmhbWyJiYXIiXV0gPC0gTlVMTApgYGAKCmBgYHtyfQptIDwtIG1hdHJpeCggYygxLDIsMyw0KSwgbnJvdz0yICkKbQpgYGAKCmBgYHtyfQpzb2x2ZShtKQp4IDwtIG1hdHJpeCggYyg2LDcpLCBucm93PTIgKQpzb2x2ZShtLCB4KQpgYGAKCmBgYHtyfQo/c29sdmUKYGBgCgpgYGB7cn0KbiA8LSAxMDAwCngxIDwtIGZhY3Rvciggc2FtcGxlKDE6MywgbiwgcmVwbGFjZT1UKSwgbGV2ZWxzPTE6MyApCngyIDwtIGZhY3Rvciggc2FtcGxlKExFVFRFUlNbMTo1XSwgbiwgcmVwbGFjZT1UKSwgbGV2ZWxzPUxFVFRFUlNbMTo1XSApCngzIDwtIGZhY3Rvciggc2FtcGxlKGMoRixUKSxuLHJlcGxhY2U9VCksIGxldmVscz1jKEYsVCkgKQpkIDwtIGRhdGEuZnJhbWUoeDEseDIseDMpCnIgPC0gdGFibGUoZCkKYGBgCgpgYGB7cn0KcgpgYGAKCmBgYHtyfQpmdGFibGUoZCkgI2Vhc2llciByZWFkaW5nCmBgYAoKYGBge3J9CiMgY29udGluZ2VuY3kgdGFibGUgaW50byBhIGRhdGEuZnJhbWUKbiA8LSAxMDAKayA8LSAxMAp4IDwtIGZhY3Rvciggc2FtcGxlKExFVFRFUlNbMTprXSwgbiwgcmVwbGFjZT1UKSwgbGV2ZWxzPUxFVFRFUlNbMTprXSApCngKZCA8LSB0YWJsZSh4KQp4MiA9IGZhY3RvciggcmVwKG5hbWVzKGQpLGQpLCBsZXZlbHM9bmFtZXMoZCkgKQp4MgpgYGAKCmBgYHtyfQojIGFwcGx5Cm9wdGlvbnMoZGlnaXRzPTQpCmRmIDwtIGRhdGEuZnJhbWUoeD1ybm9ybSgyMCkseT1ybm9ybSgyMCksej1ybm9ybSgyMCkpCmFwcGx5KGRmLDIsbWVhbikKcm93bmFtZXMoZGYpIDwtIExFVFRFUlNbMToyMF0KYXBwbHkoZGYsIDEsIG1lYW4pCmBgYAoKYGBge3J9CmdsKDIsMTAsMjApCnRhcHBseSgxOjIwLCBnbCgyLDEwLDIwKSwgc3VtKSAjIHRhcHBseTogMm5kIGFyZ3VtZW50IHVzZWQgZm9yIGdyb3VwaW5nCgpieSgxOjIwLCBnbCgyLDEwLDIwKSwgc3VtKQpgYGAKCmBgYHtyfQp4IDwtIGxpc3QoYT1ybm9ybSgxMCksIGI9cnVuaWYoMTAwKSwgYz1yZ2FtbWEoNTAsMSkpCnNhcHBseSh4LHNkKSAjIHNhcHBseTogYXBwbHkgRlVOIG9uIGVhY2ggZWxlbWVudCBvZiB2ZWN0b3IKbGFwcGx5KHgsc2QpICMgbGFwcGx5OiBzYW1lIGJ1dCByZXR1cm5zIGxpc3QKCmBgYAoKYGBge3J9CiMgRXhlcmNpc2U6IExldCB4IGJlIGEgYm9vbGVhbiB2ZWN0b3IuIENvdW50IHRoZSBudW1iZXIgb2Ygc2VxdWVuY2VzICgicnVucyIpIG9mIHplcm9zIChmb3IgaW5zdGFuY2UsIGluIDAwMTAxMDAxMDEwMTEwLCB0aGVyZSBhcmUgNiBydW5zOiAwMCAwIDAwIDAgMCAwKS4gQ291bnQgdGhlIG51bWJlciBvZiBzZXF1ZW5jZXMgb2YgMS4gQ291bnRoIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2VxdWVuY2VzLiBTYW1lIHF1ZXN0aW9uIGZvciBhIGZhY3RvciB3aXRoIG1vcmUgdGhhbSB0d28gbGV2ZWxzLgpuIDwtIDUwCnggPC0gc2FtcGxlKDA6MSwgbiwgcmVwbGFjZT1ULCBwPWMoLjIsLjgpKQp4CmRpZmYoeCwgbGFnPTEpCgoKYGBgCgpgYGB7cn0KI0xldCByIGJlIHRoZSByZXR1cm4gb2YgYSBmaW5hbmNpYWwgYXNzZXQuIFRoZSBjbHVzdGVyZWQgcmV0dXJuIGlzIHRoZSBhY2N1bXVsYXRlZCByZXR1cm4gZm9yIGEgc2VxdWVuY2Ugb2YgcmV0dXJucyBvZiB0aGUgc2FtZSBzaWduLiBUaGUgdHJlbmQgbnVtYmVyIGlzIHRoZSBudW1iZXIgb2Ygc3RlcHMgaW4gc3VjaCBhIHNlcXVlbmNlLiBUaGUgYXZlcmFnZSByZXR1cm4gaXMgdGhlaXIgcmF0aW8uIENvbXB1dGUgdGhlc2UgcXVhbnRpdGllcy4KZGF0YShFdVN0b2NrTWFya2V0cykKeCA8LSBFdVN0b2NrTWFya2V0cwojIFdlIGFyZW4ndCBpbnRlcmVzdGVkIGluIHRoZSBzcG90IHByaWNlcywgYnV0IGluIHRoZSByZXR1cm5zCiMgcmV0dXJuW2ldID0gKCBwcmljZVtpXSAtIHByaWNlW2ktMV0gKSAvIHByaWNlW2ktMV0KeSA8LSBhcHBseSh4LCAyLCBmdW5jdGlvbiAoeCkgeyBkaWZmKHgpL3hbLWxlbmd0aCh4KV0gfSkKIyBXZSBub3JtYWxpemUgdGhlIGRhdGEKeiA8LSBhcHBseSh5LCAyLCBmdW5jdGlvbiAoeCkgeyAoeC1tZWFuKHgpKS9zZCh4KSB9KQojIEEgc2luZ2xlIHRpbWUgc2VyaWVzCnIgPC0gelssMV0KIyBUaGUgcnVucwpmIDwtIGZhY3RvcihjdW1zdW0oYWJzKGRpZmYoc2lnbihyKSkpKS8yKQpyIDwtIHJbLTFdCmFjY3VtdWxhdGVkLnJldHVybiA8LSB0YXBwbHkociwgZiwgc3VtKQp0cmVuZC5udW1iZXIgPC0gdGFibGUoZikKYm94cGxvdChhYnMoYWNjdW11bGF0ZWQucmV0dXJuKSB+IHRyZW5kLm51bWJlciwgY29sPSdwaW5rJywKICAgICAgICBtYWluPSJBY2N1bXVsYXRlZCByZXR1cm4iKQpgYGAKCmBgYHtyfQojIFN0cmluZ3MKcHJpbnQoIkhlbGxvXG4iKQoKY2F0KCJIZWxsb1xuIikgI3VzZSBjYXQKYGBgCgpgYGB7cn0KcGFzdGUoIkhlbGxvIiwgIldvcmxkIiwgIiEiLCBzZXA9IiIpICNjb25jYXRlbmF0ZQpwYXN0ZSgiSGVsbG8gIiwgIiBXb3JsZCIsICIhIiwgc2VwPSIiKQpgYGAKCmBgYHtyfQp4IDwtIDUKcGFzdGUoIng9IiwgeCkKY2F0KCJ4PSIsIHgsICJcbiIsIHNlcD0iXG4iKQpgYGAKCmBgYHtyfQpzIDwtIGMoIkhlbGxvIiwgIiAiLCAiV29ybGQiLCAiISIpCnBhc3RlKHMpCnBhc3RlKHMsIHNlcD0iIikKcGFzdGUocywgY29sbGFwc2U9IiIpCmBgYAoKYGBge3J9CnBhc3RlKDE6MywgIkhlbGxvIFdvcmxkISIsIHNlcD0iOiIpCmBgYAoKYGBge3J9Cm5jaGFyKCJIZWxsbyBXb3JsZCEiKQpgYGAKCmBgYHtyfQpzIDwtICJIZWxsbyBXb3JsZCIKc3Vic3RyaW5nKHMsIDQsIDYpCmBgYAoKYGBge3J9CnMgPC0gImZvby0tPmJhci0tPmJheiIKc3Ryc3BsaXQocywgIi0tPiIpCmBgYAoKYGBge3J9CiMgUmVnZXgKcyA8LSAiZm9vLCBiYXIsIGJheiIKc3Ryc3BsaXQocywgIiwgKiIpCmBgYAoKYGBge3J9CnMgPC0gYXBwbHkobWF0cml4KExFVFRFUlNbMToyNF0sIG5yPTQpLCAyLCBwYXN0ZSwgY29sbGFwc2U9IiIpCnMKYGBgCgpgYGB7cn0KZ3JlcCgiTyIsIHMpCmdyZXAoIk8iLCBzLCB2YWx1ZT1UKQpgYGAKCmBgYHtyfQpyZWdleHByKCJvIiwgIkhlbGxvIikKYGBgCgpgYGB7cn0KcmVnZXhwcigibyIsIGMoIkhlbGxvIiwgIldvcmxkISIpKQpgYGAKCmBgYHtyfQpzIDwtICJmb28gICAgYmFyIGJheiIKZ3N1YigiICIsICIiLCBzKSAgICMgUmVtb3ZlIGFsbCB0aGUgc3BhY2VzCmdzdWIoIiArIiwgIiAiLCBzKSAgIyBSZW1vdmUgbXVsdGlwbGUgc3BhY2VzIGFuZCByZXBsYWNlIHRoZW0gYnkgc2luZ2xlIHNwYWNlcwoKYGBgCgpgYGB7cn0KI1RoZSAic3ViIiBpcyBzaW1pbGFyIHRvICJnc3ViIiBidXQgb25seSByZXBsYWNlcyB0aGUgZmlyc3Qgb2NjdXJyZW5jZS4KcyA8LSAiZm9vIGJhciBiYXoiCnN1YigiICIsICIiLCBzKQpgYGAKCmBgYHtyfQojIERhdGVzCmFzLkRhdGUoIjIwMDUtMDUtMTUiKSAjSVNPIDg2MDEKYGBgCgpgYGB7cn0KYXMuRGF0ZSgiMTUvMDUvMjAwNSIsIGZvcm1hdD0iJWQvJW0vJVkiKQphcy5EYXRlKCIxNS8wNS8wNSIsIGZvcm1hdD0iJWQvJW0vJXkiKQpjYXQoIlxuIikKYXMuRGF0ZSgiMDEvMDIvMDMiLCBmb3JtYXQ9IiV5LyVtLyVkIikKYXMuRGF0ZSgiMDEvMDIvMDMiLCBmb3JtYXQ9IiV5LyVkLyVtIikKCmBgYAoKYGBge3J9CmFzLkRhdGUoIjAxLzAyLzAzIiwgZm9ybWF0PSIleS8lbS8lZCIpIC0gYXMuRGF0ZSgiMDEvMDIvMDMiLCBmb3JtYXQ9IiV5LyVkLyVtIikKYGBgCgpgYGB7cn0KU3lzLkRhdGUoKQpmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0PSIlQSwgJWQgJUIgJVkiKQpgYGAKCmBgYHtyfQpzZXEoYXMuRGF0ZSgiMjAwNS0wMS0wMSIpLCBhcy5EYXRlKCIyMDA1LTA3LTAxIiksIGJ5PSJtb250aCIpCnNlcShhcy5EYXRlKCIyMDA1LTAxLTAxIiksIGFzLkRhdGUoIjIwMDUtMDctMDEiKSwgYnk9MzEpCmBgYAoKYGBge3J9Cm1ldGhvZHMoY2xhc3M9IkRhdGUiKQpgYGAKCmBgYHtyfQphcy5QT1NJWGx0KCIyMDA1LTA1LTE1IDIxOjQ1OjE3IikKYGBgCgpgYGB7cn0KYXMuUE9TSVhjdChTeXMuRGF0ZSgpKQpgYGAKCmBgYHtyfQojIFJlYWRpbmcgZnJvbSBkYXRhZnJhbWVzCiMgb3B0aW9uIDEKICAjZCA8LSByZWFkLnRhYmxlKCJmb28udHh0IikKICAjZCREYXRlIDwtIGFzLkRhdGUoIGFzLmNoYXJhY3RlciggZCREYXRlICkgKQojIG9wdGlvbiAyCiAgI3JlYWQudGFibGUoImZvby50eHQiLCBjb2xDbGFzc2VzPWMoIkRhdGUiLCAiY2hhcmFjdGVyIiwgcmVwKDEwLCAibnVtZXJpYyIpKSkKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9Cm9wdGlvbnMod2Fybj0xKQpgYGAKCmBgYHtyfQptZXRob2RzKHBsb3QpCmBgYAoKYGBge3J9CiMgSW1wb3J0IAoKIyBkIDwtIHJlYWQudGFibGUoImZvby50eHQiLCBoZWFkZXI9VCwgc2VwPSIsIikKIyBkIDwtIHJlYWQuY3N2KCJ0eHQuY3N2IikKIyBkIDwtIHJlYWQuY3N2MigidHh0LmNzdiIpICAjIHNlbWljb2xvbi1zZXBhcmF0ZWQgZmlsZSwgd2l0aCBhCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjb21tYSBpbnN0ZWFkIG9mIHRoZSBkZWNpbWFsIHBvaW50LgojIGQgPC0gcmVhZC5kZWxpbSgiZm9vLnR4dCIpICMgVGFiLWRlbGltaXRlZCBmaWxlCiMgZCA8LSByZWFkLmZ3ZigidHh0LmZ3ZiIpICAgIyBGaXhlZCB3aWR0aCBmaWVsZHMKYGBgCgpgYGB7cn0KIyBFeGNlbDogdGhpcyBtYXkgYmUgdHJpY2tpZXI6IHRoZSBtaXNzaW5nIHZhbHVlcyBvZnRlbiBhcHBlYXIgYXMgIiNOL0EhIiBhbmQgYXJlIG1pc3Rha2VuIGZvciB0aGUgc3RhcnQgb2YgYSBjb21tZW50Li4uIFlvdSBjYW4gdHJ5CgojIGQgPC0gcmVhZC50YWJsZSgiZm9vLmNzdiIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgCiMgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSBjKCIjTi9BISIsICJOQSIsICJATkEiKSwgCiMgICAgICAgICAgICAgICAgIHF1b3RlID0gJyInLAojICAgICAgICAgICAgICAgICBjb21tZW50LmNoYXIgPSAiIikKYGBgCgpgYGB7cn0KI0lmIHlvdXIgZmlsZSBvbmx5IGNvbnRhaW5zIG51bWJlciwgb3Igb25seSBzdHJpbmdzLCBpdCBpcyB3aXNlciB0byBzdG9yZSBpdCBpbiBhIG1hdHJpeCwgbm90IGEgZGF0YS5mcmFtZS4gVGhpcyBpcyB3aGF0IHRoZSAic2NhbiIgZnVuY3Rpb24gZG9lcy4KIyBBIG51bWVyaWMgbWF0cml4CiAgIyB4IDwtIHNjYW4oImZvby50eHQiLCBzZXA9IiwiKSAgIyBHaXZlcyBhIG51bWVyaWMgdmVjdG9yCiAgIyBuIDwtIHNjYW4oImZvby50eHQiLCBzZXA9IiwiLCBubGluZXM9MSkKICAjIHggPC0gbWF0cml4KHgsIG5jPW4pCgojIEEgdmVjdG9yIG9mIHN0cmluZ3MKICAjeCA8LSBzY2FuKCJmb28udHh0Iiwgd2hhdD1jaGFyYWN0ZXIoMCkpCgpgYGAKCmBgYHtyfQojIEJhY2sgdG9odHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvbWFudWFscy9SLWludHJvLnBkZiAtIFJlZ3Jlc3Npb24KCiAgIyBmbTA1IDwtIGxtKHkgfiB4MSArIHgyICsgeDMgKyB4NCArIHg1LCBkYXRhID0gcHJvZHVjdGlvbikKICAjIGZtNiA8LSB1cGRhdGUoZm0wNSwgLiB+IC4gKyB4NikKICAjIHNtZjYgPC0gdXBkYXRlKGZtNiwgc3FydCguKSB+IC4pCmBgYAoKYGBge3J9CiMgU3BpbmUgKGZyb20gaGVscCkKcmVxdWlyZShncmFwaGljcykKCm9wIDwtIHBhcihtZnJvdyA9IGMoMiwxKSwgbWdwID0gYygyLC44LDApLCBtYXIgPSAwLjErYygzLDMsMywxKSkKbiA8LSA5CnggPC0gMTpuCnkgPC0gcm5vcm0obikKcGxvdCh4LCB5LCBtYWluID0gcGFzdGUoInNwbGluZVtmdW5dKC4pIHRocm91Z2giLCBuLCAicG9pbnRzIikpCmxpbmVzKHNwbGluZSh4LCB5KSkKbGluZXMoc3BsaW5lKHgsIHksIG4gPSAyMTApLCBjb2wgPSAyKQpgYGAKCmBgYHtyfQojIE5BIGhhbmRsaW5nIC0gaHR0cDovL3Rob21hc2xlZXBlci5jb20vUmNvdXJzZS9UdXRvcmlhbHMvTkEuaHRtbApnMSA8LSBjKDEsIDIsIE5BLCBOQSwgTkEsIDYsIDcpCmcyIDwtIG5hLm9taXQoZzEpCmcyCmBgYAoKYGBge3J9CmF0dHJpYnV0ZXMoZzIpJG5hLmFjdGlvbgpgYGAKCmBgYHtyfQpzdW0oZzEpCnN1bShnMSwgbmEucm0gPSBUUlVFKQpgYGAKCmBgYHtyfQojIENvciAtPiBjYW4gZWxpbWluYXRlIG9ubHkgcGFpci13aXNlIE5BcyAoKQp4IDwtIGMoMSwgMiwgMywgTkEsIDUsIDcsIDkpCnkgPC0gYygzLCAyLCA0LCA1LCAxLCAzLCA0KQp6IDwtIGMoTkEsIDIsIDMsIDUsIDQsIDMsIDQpCm0gPC0gZGF0YS5mcmFtZSh4LCB5LCB6KQptCmBgYAoKYGBge3J9CmNvcihtKSAgIyByZXR1cm5zIGFsbCBOQXMKYGBgCgpgYGB7cn0KY29yKG0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSAjIERlZmF1bHQsIGFsbCByZWNvcmRzIHdpdGggTkEgcmVtb3ZlZApgYGAKCmBgYHtyfQpjb3IobSwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICNrZXB0IG1vcmUgcmVjb3JkcyBmb3IgeX54IGFuZCB5fnoKYGBgCgpgYGB7cn0KIyBEZWZhdXQgZm9yIGxtIGlzIGFsc28gY2FzZXdpc2UgZGVsZXRpb24KbG0gPC0gbG0oeSB+IHggKyB6LCBkYXRhID0gbSkKc3VtbWFyeShsbSkKYGBgCgpgYGB7cn0KIyBDaGVja2luZyBsZW5ndGggb2YgZGF0YSB1c2VkIGZvciByZWdyZXNzaW9uCmxlbmd0aChtJHkpCmxlbmd0aChsbSRmaXR0ZWQpCmBgYAoKYGBge3J9CiMgY2hlY2tpbmcgd2hlcmUgbWlzc2luZyBkYXRhIGlzCmlzLm5hKG0pICMgb25seSB1c2VmdWwgZm9yIHNtYWxsIGV4YW1wbGVzCiMgaW1hZ2UKaW1hZ2UoaXMubmEobSksIG1haW4gPSAiTWlzc2luZyBWYWx1ZXMiLCB4bGFiID0gIk9ic2VydmF0aW9uIiwgeWxhYiA9ICJWYXJpYWJsZSIsIAogICAgeGF4dCA9ICJuIiwgeWF4dCA9ICJuIiwgYnR5ID0gIm4iKQpheGlzKDEsIHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gbnJvdyhtKSksIDE6bnJvdyhtKSwgY29sID0gIndoaXRlIikKYXhpcygyLCBjKDAsIDAuNSwgMSksIG5hbWVzKG0pLCBjb2wgPSAid2hpdGUiLCBsYXMgPSAyKQpgYGAKCmBgYHtyfQojIHRvIHJlbW92ZSBjYXNld2lzZToKbTIgPC0gbmEub21pdChtKSAjIHVzZSBuZXcgdmFyaWFibGUgdG8ga2VlcCBvcmlnaW5hbCBkYXRhZnJhbWUKbQptMgpgYGAKCmBgYHtyfQojIE1lYW4gaW1wdXRhdGlvbgp4MiA8LSB4CngyW2lzLm5hKHgyKV0gPC0gbWVhbih4MiwgbmEucm0gPSBUUlVFKQp4CngyCmBgYAoKYGBge3J9CiMgUmFuZG9tIGltcHV0YXRpb24gLSBjb25zZXJ2ZSBtZWFuIGFuZCB2YXJpYW5jZS4gCiMgSG93OiBzYW1wbGUgcmVzdCBvZiB0aGUgdmFsdWVzIHRvIGZpbGwgTkFzCngzIDwtIHgKeDNbaXMubmEoeDMpXSA8LSBzYW1wbGUoeDNbIWlzLm5hKHgzKV0sIHN1bShpcy5uYSh4MykpLCBUUlVFKQp4CngzCmBgYAoKYGBge3J9CiMgU2F2aW5nIFIgZGF0YSBodHRwOi8vdGhvbWFzbGVlcGVyLmNvbS9SY291cnNlL1R1dG9yaWFscy9zYXZpbmdkYXRhLmh0bWwgCnNldC5zZWVkKDEpCm15ZGYgPC0gZGF0YS5mcmFtZSh4ID0gcm5vcm0oMTApLCB5ID0gcm5vcm0oMTApLCB6ID0gcm5vcm0oMTApKQpgYGAKCmBgYHtyfQpzYXZlKG15ZGYsIGZpbGUgPSAic2F2ZWRkZi5SRGF0YSIpICMgY2FuIGJlIGxvYWRlZCBieSBkb3VibGUtY2xpY2tpbmcgb24gc2F2ZWQgZmlsZQpgYGAKCmBgYHtyfQp1bmxpbmsoInNhdmVkZGYuUkRhdGEiKSAjIHJlbW92aW5nIGZpbGUKYGBgCgpgYGB7cn0KIyBkcHV0IHRvIGhhdmUgYSByZWFkYWJsZSBmb3JtYXQgKGUuZy4gZm9yIHN0YWNrIG92ZXJmbG93KQpkcHV0KG15ZGYpCmBgYAoKYGBge3J9CmRwdXQobXlkZiwgInNhdmVkZGYudHh0IikKYGBgCgpgYGB7cn0KbXlkZjIgPC0gZGdldCgic2F2ZWRkZi50eHQiKQpteWRmMgpgYGAKCmBgYHtyfQpteWRmPT1teWRmMiAjIGR1ZSB0byByb3VuZGluZwpgYGAKCmBgYHtyfQp1bmxpbmsoInNhdmVkZGYudGV4dCIpCmBgYAoKYGBge3J9CiMgY3N2CndyaXRlLmNzdihteWRmLCBmaWxlID0gInNhdmVkZGYuY3N2IikKdW5saW5rKCJzYXZlZGYuY3N2IikKYGBgCgpgYGB7cn0KIyBEYXRhZnJhbWUgcmVhcnJhbmdlbWVudAoKc2V0LnNlZWQoNTApCm15ZGYgPC0gZGF0YS5mcmFtZShhID0gcmVwKDE6MiwgZWFjaCA9IDEwKSwgYiA9IHJlcCgxOjQsIHRpbWVzID0gNSksIGMgPSBybm9ybSgyMCksIAogICAgZCA9IHJub3JtKDIwKSwgZSA9IHNhbXBsZSgxOjIwLCAyMCwgRkFMU0UpKQpoZWFkKG15ZGYpCmBgYAoKYGBge3J9CiMgbWFudWFsIG9yZGVyIGNoYW5nZQoKaGVhZChteWRmWywgYygiYyIsICJkIiwgImUiLCAiYSIsICJiIildKQojIG15ZGYgPC0gbXlkZlssIGMoMywgNCwgNSwgMSwgMildCmBgYAoKYGBge3J9CiMgdXNpbmcgb3JkZXIKb3JkZXIobXlkZiRlKQpoZWFkKG15ZGZbb3JkZXIobXlkZiRlKSwgXSkgIyBvcmRlcmluZyBvbiBhbnkgY29sdW1uCmBgYAoKYGBge3J9CiMgU3Vic2V0CgpteWRmW215ZGYkYSA9PSAxLCBdCm15ZGZbbXlkZiRhID09IDEgJiBteWRmJGIgPiAyLCBdCgpzdWJzZXQobXlkZiwgYSA9PSAxICYgYiA+IDIpCnN1YnNldChteWRmLCBzZWxlY3QgPSBjKCJhIiwgImIiKSkKc3Vic2V0KG15ZGYsIGEgPT0gMSAmIGIgPiAyLCBzZWxlY3QgPSBjKCJjIiwgImQiKSkgIyBmaWx0ZXIgcm93cyAmIGNvbHVtbnMKCmBgYAoKYGBge3J9CiMgU3BsaXR0aW5nIAoKc3BsaXQobXlkZiwgbXlkZiRhKSAjIC0+IHNwbGl0dGluZyBhY2NvcmRpbmcgdG8gdmFsdWVzIG9mIGEKYGBgCgpgYGB7cn0Kc3BsaXQobXlkZiwgbGlzdChteWRmJGEsIG15ZGYkYikpCmBgYAoKYGBge3J9CmxhcHBseShzcGxpdChteWRmLCBteWRmJGEpLCBzdW1tYXJ5KSAjIHBlcmZvcm0gc3VtbWFyeSBvbiBlYWNoIG9mIHRoZSBkYXRhZnJhbWVzCmBgYAoKYGBge3J9CiMgc2FtcGxpbmc6IHNwbGl0dGluZyBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldAojIE9wdGlvbiAxCnMgPC0gc2FtcGxlKDE6bnJvdyhteWRmKSwgNSwgRikgI25vIHJlcGxhY2VtZW50CnMKYGBgCgpgYGB7cn0KIyB1c2UgNSByb3dzIGFzIHRyYWluaW5nIHNldApteWRmW3MsXQpgYGAKCmBgYHtyfQojIHRlc3Qgc2V0Cm15ZGZbLXMsIF0KYGBgCgpgYGB7cn0KIyBPcHRpb24gMgpzMiA8LSByYmlub20obnJvdyhteWRmKSwgMSwgMC4yKSAjd29uJ3QgbmVjZXNzYXJpbHkgZ2l2ZSBleGFjdGx5IDUgcm93cwpzMgpgYGAKCmBgYHtyfQpteWRmW3MyLF0KbXlkZlstczIsXQpgYGAKCmBgYHtyfQojIFJlY29kaW5nIHZlY3RvcnMKbGlicmFyeShjYXIpCmBgYAoKYGBge3J9CmIgPC0gMToyMAojaCA8LSByZWNvZGUoYiwgIjE6NT0xOiA2OjEwPTI7IGVsc2U9TkEiKSAjIGluY3JlZGlibHkgdGhpcyBjcmVhdGVzIGFuIGVycm9yCmUgPC0gcmVjb2RlKGIsICIxOjU9MTsgNjoxMD0yOyBlbHNlPU5BIikKZQpmIDwtIHJlY29kZShiLCAibG86NT0xOyA2OjEwPTI7IDExOjE1PTM7IDE2OmhpPTQ7IGVsc2U9TkEiKQpmCmBgYAoKYGBge3J9CmUgPC0gcmVjb2RlKGgsICJOQT05OSIpCmUKYGBgCgpgYGB7cn0KIyBSZWNvbmRpbmcgb24gbXVsdGlwbGUgdmFyaWFibGVzCmkgPC0gZXhwYW5kLmdyaWQoMTo0LCAxOjIpCmkKYGBgCgpgYGB7cn0KaW50ZXJhY3Rpb24oaSRWYXIxLCBpJFZhcjIpICMgY3JlYXRlcyBhbGwgcG9zc2libGUgY29tYmluYXRpb25zCmBgYAoKYGBge3J9CiMgU2NhbGluZwpzZXQuc2VlZCgxKQpuIDwtIDMwCm15ZGYgPC0gZGF0YS5mcmFtZSh4MSA9IHJiaW5vbShuLCAxLCAwLjUpLCB4MiA9IHJiaW5vbShuLCAxLCAwLjEpLCB4MyA9IHJiaW5vbShuLCAKICAgIDEsIDAuNSksIHg0ID0gcmJpbm9tKG4sIDEsIDAuOCksIHg1ID0gMSwgeDYgPSBzYW1wbGUoYygwLCAxLCBOQSksIG4sIFRSVUUpKQpgYGAKCmBgYHtyfQpzdHIobXlkZikKYGBgCgpgYGB7cn0KbXlkZiR4MSArIG15ZGYkeDIgLSBteWRmJHgzICMgdmVjdG9yIG9wZXJhdGlvbnMKd2l0aChteWRmLCB4MSt4Mi14MykgIyB3aXRoIHRvIGluZGljYXRlIGRhdGFmcmFtZQoKYGBgCgpgYGB7cn0Kcm93U3VtcyhteWRmKQpyb3dTdW1zKG15ZGYsIG5hLnJtID0gVCkKZGF0YS5mcmFtZSgxOm4sIHJvd1N1bXMobXlkZiwgbmEucm0gPSBUKSkKYGBgCgpgYGB7cn0Kcm93TWVhbnMobXlkZikKYGBgCgpgYGB7cn0KYXBwbHkobXlkZiwgMSwgdmFyKSAjIDJuZCBhcmd1bWVudDogMSBmb3Igcm93cywgMiBmb3IgY29sdW1ucywgYygxLCAyKSAgcm93cyAmIGNvbHVtbnMuCmFwcGx5KG15ZGYsIDIsIHZhcikKc2FwcGx5KG15ZGYsIHZhcikgIyBvdmVyIGxpc3Qgb3IgdmVjdG9yCmBgYAoKYGBge3J9CiMgYWRkaW5nIGEgdmFyaWFibGUKbmV3dmFyIDwtIG51bWVyaWMobnJvdyhteWRmKSkKbmV3dmFyW215ZGYkeDEgPT0gMV0gPC0gd2l0aChteWRmW215ZGYkeDEgPT0gMSwgXSwgeDIgKyB4MykKbmV3dmFyW215ZGYkeDEgPT0gMF0gPC0gd2l0aChteWRmW215ZGYkeDEgPT0gMCwgXSwgeDMgKyB4NCArIHg1KQpuZXd2YXIKYGBgCgpgYGB7cn0KbmV3dmFyW215ZGYkeDEgPT0gMV0gPC0gd2l0aChteWRmLCB4MiArIHgzKSAjIGhlcmUgZGlmZmVyZW50IGxlbmd0aHMgIQpgYGAKCmBgYHtyfQojIE1hdHJpY2VzCnNldC5zZWVkKDEpCmEgPC0gcm5vcm0oMTAwKQpxdWFudGlsZShhLCBjKDAuMDI1LCAwLjk3NSkpCnF1YW50aWxlKGEsIHNlcSgwLCAxLCBieSA9IDAuMSkpCmBgYAoKYGBge3J9CnN1bW1hcnkoYXMubG9naWNhbChyYmlub20oMTAwMCwgMSwgMC41KSkpCmBgYAoKYGBge3J9CnN1bW1hcnkoZmFjdG9yKGEpKSAjIGZvciBmYWN0b3IsIHJldHVybnMgYWxsIHZhbHVlCmBgYAoKYGBge3J9CiMgVGFibGVzCnNldC5zZWVkKDEpCmEgPC0gc2FtcGxlKDE6NSwgMjUsIFQpCmEKYGBgCgpgYGB7cn0KdGFibGUoYSkKYGBgCgpgYGB7cn0KcHJvcC50YWJsZSh0YWJsZShhKSkgIyB0byBvYnRhaW4gcGVyY2VudGFnZXMKcHJvcC50YWJsZSh0YWJsZShhKSkgKjEwMApgYGAKCmBgYHtyfQpjYmluZCh0YWJsZShhKSwgcHJvcC50YWJsZSh0YWJsZShhKSkqMTAwKQpgYGAKCmBgYHtyfQojIG11bHRpLXZhcmlhdGUKYiA8LSByZXAoYygxLCAyKSwgbGVuZ3RoID0gMjUpCnRhYmxlKGEsIGIpCmBgYAoKYGBge3J9CmMgPC0gcmVwKGMoMywgNCwgNSksIGxlbmd0aCA9IDI1KQp0YWJsZShhLCBiLCBjKQpgYGAKCmBgYHtyfQpmdGFibGUoYSwgYywgYykgIyBwcm92aWRlcyBtb3JlIGNvbXBhY3QgZm9ybWF0CmBgYAoKYGBge3J9Cnh0YWJzKH5hICsgYikgIyByaWdodCBoYW5kIGZvcm11bGFzIHNhbWUgYXMgdGFibGUKYGBgCgpgYGB7cn0KeCA8LSB0YWJsZShhLCBiKQphZGRtYXJnaW5zKHgpCmBgYAoKYGBge3J9CnByb3AudGFibGUodGFibGUoYSwgYiksIDEpICMgcHJvcG9ydGlvbnMgYnkgcm93cwpwcm9wLnRhYmxlKHRhYmxlKGEsIGIpLCAyKQpgYGAKCmBgYHtyfQojIENvcnJlbGF0aW9ucwpzZXQuc2VlZCgxKQpuIDwtIDEwMDAKeDEgPC0gcm5vcm0obiwgLTEsIDEwKQp4MiA8LSBybm9ybShuLCAzLCAyKQp5IDwtIDUgKiB4MSArIHgyICsgcm5vcm0obiwgMSwgMikKYGBgCgpgYGB7cn0KY29yKHgxLCB4MikKY29yLnRlc3QoeDEsIHgyKQpgYGAKCmBgYHtyfQpjb3IoY2JpbmQoeDEsIHgyLCB5KSkgIyBpbnB1dCBpcyBtYXRyaXggZm9yIGNvcnJlbGF0aW9uIG1hdHJpeApgYGAKCmBgYHtyfQphIDwtIHJub3JtKG4pCmIgPC0gYV4yICsgcm5vcm0obikKcGxvdChifmEpCmBgYAoKYGBge3J9CnBsb3QoYiB+IGEsIGNvbCA9ICJncmF5IikKY3VydmUoKHgpLCBjb2wgPSAicmVkIiwgYWRkID0gVFJVRSkKY3VydmUoKHheMiksIGNvbCA9ICJibHVlIiwgYWRkID0gVFJVRSkKYGBgCgpgYGB7cn0KY29yKGEsIGIpCmNvcihhXjIsIGIpCmBgYAoKYGBge3J9CnBsb3QoYn5JKGFeMiksIGNvbCA9ICJvcmFuZ2UiKQphYmxpbmUobG0oYn5JKGFeMikpLCBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KbGF5b3V0KG1hdHJpeCgxOjIsIG5yb3cgPSAxKSkKcGxvdChiIH4gYSwgY29sID0gImdyYXkiKQpjdXJ2ZSgoeF4yKSwgY29sID0gImJsdWUiLCBhZGQgPSBUUlVFKQpwbG90KGIgfiBJKGFeMiksIGNvbCA9ICJncmF5IikKY3VydmUoKHgpLCBjb2wgPSAiYmx1ZSIsIGFkZCA9IFRSVUUpCmBgYAoKYGBge3J9CiMgUm91bmRpbmcKaGVpZ2h0IDwtIGMoMTY3LCAxNjQsIDE3MiwgMTU4LCAxODEsIDE3OSkKbWVhbihoZWlnaHQpCmBgYAoKYGBge3J9CnNpZ25pZihtZWFuKGhlaWdodCksIDQpCnJvdW5kKG1lYW4oaGVpZ2h0KSwgMSkKcm91bmQobWVhbihoZWlnaHQpLCAtMikKYGBgCgpgYGB7cn0Kb3B0aW9ucyhkaWdpdHMgPSA1KQpzZChoZWlnaHQpCm9wdGlvbnMoZGlnaXRzID0gMikKc2QoaGVpZ2h0KQpgYGAKCmBgYHtyfQpvcHRpb25zKHNjaXBlbiA9IC0xMCkgI3Bvc2l0aXZlIHZhbHVlIHRvIGdldCBmaXhlZCBub3RhdGlvbgoxMDAwMDAwMApgYGAKCmBgYHtyfQojIHNwcmludGYKc3ByaW50ZigiJS4zZiIsIHBpKQpzcHJpbnRmKCIlMDUuMWYiLCBwaSkKYGBgCgpgYGB7cn0KIyBQbG90cyBhcyBkYXRhIHN1bW1hcnkKc2V0LnNlZWQoMSkKYSA8LSBybm9ybSgzMCkKaGlzdChhLCBjb2wgPSAiZ3JheTIwIiwgYm9yZGVyID0gImxpZ2h0Z3JheSIpCmBgYAoKYGBge3J9CmRlbnNpdHkoYSkKcGxvdChkZW5zaXR5KGEpKQpgYGAKCmBgYHtyfQpoaXN0KGEsIGZyZXEgPSBGQUxTRSwgY29sID0gImdyYXkyMCIsIGJvcmRlciA9ICJsaWdodGdyYXkiKQpsaW5lcyhkZW5zaXR5KGEpLCBjb2wgPSAicmVkIiwgbHdkID0gMikKYGBgCgpgYGB7cn0KYiA8LSBjKDMsIDQuNSwgNSwgOCwgMywgNikKYmFycGxvdChiLCBuYW1lcy5hcmcgPSBsZXR0ZXJzWzE6bGVuZ3RoKGIpXSwgaG9yaXogPSBGKQpgYGAKCmBgYHtyfQpkIDwtIHJiaW5kKGMoMiwgNCwgMSksIGMoNiwgMSwgMykpCmQKYmFycGxvdChkLCBuYW1lcy5hcmcgPSBsZXR0ZXJzWzE6M10pCmBgYAoKYGBge3J9CmJhcnBsb3QoZCwgYmVzaWRlID0gVCkKYGBgCgpgYGB7cn0KbGF5b3V0KG1hdHJpeCgxOjIsIG5yb3cgPSAxKSkKYmFycGxvdChiLCBuYW1lcy5hcmcgPSBsZXR0ZXJzWzE6Nl0sIGhvcml6ID0gVFJVRSwgbGFzID0gMikKZG90Y2hhcnQoYiwgbGFiZWxzID0gbGV0dGVyc1sxOjZdLCB4bGltID0gYygwLCA4KSkKYGBgCgpgYGB7cn0KYm94cGxvdChhKQpgYGAKCmBgYHtyfQplIDwtIHJub3JtKDEwMCwgMSwgMSkKZiA8LSBybm9ybSgxMDAsIDIsIDQpCmJveHBsb3QoZSwgZikKYGBgCgpgYGB7cn0KZzEgPC0gYyhlLCBmKQpnMiA8LSByZXAoYygxLCAyKSwgZWFjaCA9IDEwMCkKYm94cGxvdChnMSB+IGcyKQpgYGAKCmBgYHtyfQojIFNjYXR0ZXJwbG90CngxIDwtIHJub3JtKDEwMDApCngyIDwtIHJub3JtKDEwMDApCngzIDwtIHgxICsgeDIKeDQgPC0geDEgKyB4MwpgYGAKCmBgYHtyfQpwbG90KHgxLCB4MikKcGxvdCh4Mn54MSkKYGBgCgpgYGB7cn0KbGF5b3V0KG1hdHJpeCgxOjMsIG5yb3cgPSAxKSkKcGxvdCh4MSwgeDIpCnBsb3QoeDEsIHgzKQpwbG90KHgxLCB4NCkKYGBgCgpgYGB7cn0KcGFpcnMofngxICsgeDIgKyB4MyArIHg0KQpgYGAKCmBgYHtyfQpjb2xvcnMoKVsxOjEwXQpsZW5ndGgoY29sb3JzKCkpCmNvbG9ycygpWzYwMF0KYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTAwKQp6IDwtIHNhbXBsZSgxOjQsIDEwMCwgVFJVRSkKeCA8LSBybm9ybSgxMDApCnkgPC0gcm5vcm0oMTAwKQpwbG90KHgsIHksIHBjaCA9IDE1LCBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIpKQpgYGAKCmBgYHtyfQpjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIsICJvcmFuZ2UiKVt6XQpwbG90KHgsIHksIHBjaCA9IDE1LCBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIsICJvcmFuZ2UiKVt6XSkgI2luZGV4aW5nIGNvbG9ycyBvbiB6IGdyb3VwcwpgYGAKCmBgYHtyfQojIEFuYWx5c2lzIG9mIHZhcmlhbmNlIAoKc2V0LnNlZWQoMTAwKQp0ciA8LSByZXAoMTo0LCBlYWNoID0gMzApCnkgPC0gbnVtZXJpYyhsZW5ndGggPSAxMjApCnlbdHIgPT0gMV0gPC0gcm5vcm0oMzAsIDUsIDEpCnlbdHIgPT0gMl0gPC0gcm5vcm0oMzAsIDQsIDIpCnlbdHIgPT0gM10gPC0gcm5vcm0oMzAsIDQsIDUpCnlbdHIgPT0gNF0gPC0gcm5vcm0oMzAsIDEsIDIpCmBgYAoKYGBge3J9CmFvdih5fnRyKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGFvdih5IH4gZmFjdG9yKHRyKSkpCmBgYAoKYGBge3J9Cm9uZXdheS50ZXN0KHkgfiB0cikKYGBgCgpgYGB7cn0Kb25ld2F5LnRlc3QoeSB+IGZhY3Rvcih0ciksIHZhci5lcXVhbCA9IFRSVUUpCmBgYAoKYGBge3J9CmJ5KHksIHRyLCBGVU4gPSBtZWFuKQpgYGAKCmBgYHtyfQp0YXBwbHkoeSwgdHIsIEZVTiA9IG1lYW4pICMgc2FtZSB0aGluZwpgYGAKCmBgYHtyfQojIERpc3RyaWJ1dGlvbnMKb3B0aW9ucyhzY2lwZW4gPSBGKQpvcHRpb25zKGRpZ2l0cyA9IDUpCmRub3JtKDApICAjIGRlbnNpdHkKZG5vcm0oMCwgbWVhbj0tMSkKYGBgCgpgYGB7cn0KcG5vcm0oMCkgICMgY3VtdWxhdGl2ZQpwbm9ybSgxLjY1KSAjIDk1JSBub3JtYWwgY29uZmlkZW5jZSBpbnRlcnZhbApwbm9ybSgxLjk2KQpwbm9ybSgxLjk2KSAtIHBub3JtKC0xLjk2KQpgYGAKCmBgYHtyfQpxbm9ybShjKDAuMDI1LCAwLjk3NSkpICAjIHF1YW50aWxlCnBub3JtKHFub3JtKGMoMC4wMjUsIDAuOTc1KSkpCmBgYAoKYGBge3J9CiMgb3RoZXIgZGlzdHJpYnV0aW9uCmRiaW5vbSgwLCAxLCAwLjUpCnBiaW5vbSgwLCAxLCAwLjUpCnFiaW5vbSguOTUsIDEsIDAuNSkgIyBiZWNhdXNlIGJpbm9taWFsIGRpc2NyZXRlIGRpc3RyaWJ1dGlvbgpgYGAKCmBgYHtyfQojIEZvcm11bGFlCm15Zm9ybXVsYSA8LSB+eApjbGFzcyhteWZvcm11bGEpCmBgYAoKYGBge3J9CiMgaW50ZXJhY3Rpb25zCnkgfiB4MSAqIHgyCnkgfiB4MTp4MiAjIHdpdGhvdXQgdGhlIHZhcmlhYmxlcyB0aGVtc2VsdmVzIAp5IH4gLTEgKyB4MSAqIHgyICMgZHJvcCB0aGUgaW50ZXJjZXB0CnkgfiB4ICsgSSh4XjIpICMgd2l0aG91dCBJKCkgUiB0aGlua3MgeF4yIGlzIGEgZHVwbGljYXRlCmBgYAoKYGBge3J9CiMgQXMgc3RyaW5ncwooInkgfiB4IikgPT0gKHkgfiB4KQphcy5mb3JtdWxhKCJ5fngiKQphcy5jaGFyYWN0ZXIoeSB+IHgpICMgRm9ybXVsYSBpbmRleGVkIHdpdGggb3BlcmFuZCBmaXJzdApgYGAKCmBgYHtyfQp0ZXJtcyh5IH4geDEgKyB4MikKYGBgCgpgYGB7cn0KdXBkYXRlKHkgfiB4LCB+LiArIHgyKQp1cGRhdGUoeSB+IHgsIHogfiAuKQpgYGAKCmBgYHtyfQojIEJpdmFyaWF0ZSBSZWdyZXNzaW9uCgpzZXQuc2VlZCgxKQpiaW4gPC0gcmJpbm9tKDEwMDAsIDEsIDAuNSkKCm91dCA8LSAyICogYmluICsgcm5vcm0oMTAwMCkKCmJ5KG91dCwgYmluLCBtZWFuKQpgYGAKCmBgYHtyfQp0LnRlc3Qob3V0IH4gYmluKQpgYGAKCmBgYHtyfQpsbShvdXQgfiBiaW4pICMgc2xvcGUgPSBtZWFuIGRpZmZlcmVuY2UKYGBgCgpgYGB7cn0Kc3VtbWFyeShsbShvdXR+YmluKSkkY29lZgpgYGAKCmBgYHtyfQpwbG90KG91dCB+IGJpbiwgY29sID0gImdyYXkiKQpwb2ludHMoMDoxLCBieShvdXQsIGJpbiwgbWVhbiksIGNvbCA9ICJibHVlIiwgYmcgPSAiYmx1ZSIsIHBjaCA9IDIzKQphYmxpbmUoY29lZihsbShvdXQgfiBiaW4pKSwgY29sID0gImJsdWUiKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCgxKQp4IDwtIHJ1bmlmKDEwMDAsIDAsIDEwKQp5IDwtIDMgKiB4ICsgcm5vcm0oMTAwMCwgMCwgNSkKYGBgCgpgYGB7cn0KIyBnbG0gcGxvdHMKc2V0LnNlZWQoMSkKbiA8LSAxMDAKeCA8LSBydW5pZihuLCAwLCAxKQp5IDwtIHJiaW5vbShuLCAxLCB4KSAjIG1vcmUgb3V0Y29tZXMgb2YgMSBhcyB4IC0+IDEKYGBgCgpgYGB7cn0KcGxvdCh5IH4geCwgY29sID0gTlVMTCwgYmcgPSByZ2IoMCwgMCwgMCwgMC41KSwgcGNoID0gMjEpICMgYmc6IGJhY2tncm91bmQgY29sb3IgKGZvciBwb2ludHMpCmFibGluZShsbSh5IH4geCksIGx3ZCA9IDIpICMgbHdkOiBsaW5lIHdpZHRoIChkZWZhdWx0OiAxKQojIGhlcmUgbGluZWFyIGRvZXNuJ3Qgd29yawpgYGAKCmBgYHtyfQptMSA8LSBnbG0oeSB+IHgsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkKYGBgCgpgYGB7cn0KbmV3ZGYgPC0gZGF0YS5mcmFtZSh4ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApKQpuZXdkZgpgYGAKCmBgYHtyfQpuZXdkZiRwb3V0X2xvZ2l0IDwtIHByZWRpY3QobTEsIG5ld2RmLCBzZS5maXQgPSBUUlVFLCB0eXBlID0gInJlc3BvbnNlIikkZml0Cm5ld2RmWzk1OjEwMCxdCmBgYAoKYGBge3J9CiMgYnVpbGQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZnJvbSBzdGFuZGFyZCBlcnJvcgpuZXdkZiRwc2VfbG9naXQgPC0gcHJlZGljdChtMSwgbmV3ZGYsIHNlLmZpdCA9IFRSVUUsIHR5cGUgPSAicmVzcG9uc2UiKSRzZS5maXQKbmV3ZGYkcGxvd2VyX2xvZ2l0IDwtIG5ld2RmJHBvdXRfbG9naXQgLSAoMS45NiAqIG5ld2RmJHBzZV9sb2dpdCkgICMgOTUlIENJIGxvd2VyIGJvdW5kCm5ld2RmJHB1cHBlcl9sb2dpdCA8LSBuZXdkZiRwb3V0X2xvZ2l0ICsgKDEuOTYgKiBuZXdkZiRwc2VfbG9naXQpICAjIDk1JSBDSSB1cHBlciBib3VuZAojIHFub3JtKGMoMC4wMjUsIDAuOTc1KSkgPSAoLTEuOTYsICsxLjk2KQpgYGAKCmBgYHtyfQpuZXdkZlsxOjEwLGMoMSwyLDMsNSw0KV0KYGBgCgpgYGB7cn0KIyBub3cgcGxvdCBwcmVkaWN0ZWQgdmFsdWVzCndpdGgobmV3ZGYsIHBsb3QocG91dF9sb2dpdCB+IHgsIHR5cGUgPSAibCIsIGx3ZCA9IDIpKQp3aXRoKG5ld2RmLCBsaW5lcyhwdXBwZXJfbG9naXQgfiB4LCB0eXBlID0gImwiLCBsdHkgPSAyKSkKd2l0aChuZXdkZiwgbGluZXMocGxvd2VyX2xvZ2l0IH4geCwgdHlwZSA9ICJsIiwgbHR5ID0gMikpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKIyBodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8KCiMgYmFzaWMgUidzIHBsb3QKcGxvdChpcmlzJFNlcGFsLldpZHRoLCBpcmlzJFNlcGFsLkxlbmd0aCkKYGBgCgpgYGB7cn0KaGVhZChtcGcpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkKZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICMgd2l0aCByZWdyZXNzaW9uCmBgYAoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgIyBhZGRpbmcgYSBkaW1lbnNpb24KZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjbGFzcykpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGNsYXNzKSkKZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoYWVzKGFscGhhID0gY2xhc3MpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfZ3JpZCguIH4gY3lsKSAjIDJEIGdyaWQsIHJvd3MgfiBjb2xzCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF9ncmlkKGRydiB+IC4pIApnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfZ3JpZChkcnYgfiBjeWwpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKCB+IGNsYXNzKQojIGZhY2V0X3dyYXAgd3JhcHMgYSAxZCBzZXF1ZW5jZSBvZiBwYW5lbHMgaW50byAyZC4gVGhpcyBpcyBnZW5lcmFsbHkgYSBiZXR0ZXIgdXNlIG9mIHNjcmVlbiBzcGFjZSB0aGFuIGZhY2V0X2dyaWQgYmVjYXVzZSBtb3N0IGRpc3BsYXlzIGFyZSByb3VnaGx5IHJlY3Rhbmd1bGFyLgpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICAjIFR1cm4gb2ZmIGNvbmZpZGVuY2UgYmFuZApgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBjbGFzcywgeSA9IGh3eSkpICsgZ2VvbV9ib3hwbG90KCkgIyBzY2F0dGVycGxvdApnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSByZW9yZGVyKGNsYXNzLCBod3kpLCB5ID0gaHd5KSkgKyBnZW9tX2JveHBsb3QoKSAjIHJlb3JkZXIgKG1lYW4pCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgbWVkaWFuKSwgeSA9IGh3eSkpICsgZ2VvbV9ib3hwbG90KCkgIyByZW9yZGVyIGJ5IG1lZGlhbgpgYGAKYGBge3J9CiMgaml0dGVyCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludCgpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBjdHksIHkgPSBod3kpKSArIGdlb21faml0dGVyKCkgIyBpZGVudGljYWwgb3B0aW9uCmBgYApgYGB7cn0KbmFtZXMoZGlhbW9uZHMpICMgcGFydCBvZiBnZ3Bsb3QyIHBhY2thZ2UKZ2dwbG90KGRhdGEgPSBkaWFtb25kcyxhZXMoeCA9Y3V0KSkgKyBnZW9tX2JhcihhZXMoZmlsbCA9Y3V0KSkgIyBmaWxsOiBjb2xvciBpbnNpZGUgb2YgYmFycwpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9Y3V0KSkgKyBnZW9tX2JhcihhZXMoY29sb3IgPWN1dCkpICMgY29sb3I6IGxpbmUgYXJvdW5kIHRoZSBiYXJzCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY29sb3IpKSArIGdlb21fYmFyKGFlcyhmaWxsID0gY3V0KSwgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCgoKYGBge3J9CnN0cihkaWFtb25kcykKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEpCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQpKSArIGdlb21faGlzdG9ncmFtKCkgI3N0YXRfYmluOiBiaW53aWR0aCBkZWZhdWx0ZWQgdG8gcmFuZ2UvMzAuCmBgYAoKYGBge3J9Cnpvb20gPC0gY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDU1LCA3MCkpCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gZGVwdGgpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yKSArIHpvb20KYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBkZXB0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBjdXQpLCBiaW53aWR0aCA9IDAuMSkgKyB6b29tCmBgYAoKYGBge3J9CiMgdG8gY29tcGFyZSB2YXJpYWJsZXMKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBkZXB0aCkpICsgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsID0gY3V0KSkgKyB6b29tCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gZGVwdGgpKSArIGdlb21fZGVuc2l0eShhZXMoY29sb3IgPSBjdXQsIGZpbGwgPSBjdXQsIGFscGhhPTAuMSkpICsgem9vbQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IHByaWNlKSkgK2dlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gY3V0KSwgYmlud2lkdGggPSAxMDApCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gcHJpY2UpKSArIGdlb21fZGVuc2l0eShhZXMoY29sb3I9IGN1dCkpICMgYmV0dGVyCmBgYAoKYGBge3J9CiMgVmlzdWFsaXphdGlvbiBvZiBiaWcgZGF0YQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY3V0KSkgIyBub3QgaGVscGZ1bApnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fYmluMmQoKQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fZGVuc2l0eTJkKCkKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2RlbnNpdHkyZCgpCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gY3V0KSkKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBjdXQpLCBtZXRob2QgPSAibG9lc3MiLCBzZT1GKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpCmBgYAoKYGBge3J9CiMgZ2dzYXZlKCJteS1wbG90LnBkZiIpICMgUERGIG1vcmUgY3Jpc3AKIyBnZ3NhdmUoIm15LXBsb3QucG5nIikKIyBnZ3NhdmUoIm15LXBsb3QucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQojIGdnc2F2ZSgibXktcGxvdC5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCmBgYAoKYGBge3J9CiMgTW9yZSBwbG90IGV4YW1wbGVzCmJuYW1lcyA9IHJlYWQuY3N2KCJibmFtZXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAKYmlydGhzID0gcmVhZC5jc3YoImJpcnRocy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmhlYWQoYmlydGhzLCAzKQpoZWFkKGJuYW1lcywgMykKYGBgCgpgYGB7cn0KIyBTaW1wbGUgcGxvdApRdWVudGluIDwtIGJuYW1lc1tibmFtZXMkbmFtZSA9PSAiTGV0aXRpYSIsIF0gCnFwbG90KHllYXIsIHByb3AsIGRhdGEgPSBRdWVudGluLCBnZW9tID0gImxpbmUiKQpgYGAKCmBgYHtyfQojIEludGVyYWN0aW9ucwptaWNoYWVscyA8LSBibmFtZXNbYm5hbWVzJG5hbWUgPT0gIlF1ZW50aW4iIHwgYm5hbWVzJG5hbWUgPT0gIkFsZXhpcyIgfCBibmFtZXMkbmFtZSA9PSAiR2luYSIsIF0KcXBsb3QoeWVhciwgcHJvcCwgZGF0YSA9IG1pY2hhZWxzLCBnZW9tID0gImxpbmUiLCBjb2xvciA9IGludGVyYWN0aW9uKHNleCwgbmFtZSkpCmBgYAoKYGBge3J9CiMgZHBseXIKbGlicmFyeShkcGx5cikKYm5hbWVzID0gdGJsX2RmKGJuYW1lcykgCmJpcnRocyA9IHRibF9kZihiaXJ0aHMpIApjbGFzcyhibmFtZXMpCmBgYAoKYGBge3J9CiMgZmlsdGVyCmZpbHRlcihibmFtZXMsIHNleCA9PSAiZ2lybCIgJiAoeWVhciA9PSAxOTAwIHwgeWVhciA9PSAyMDAwKSkKYGBgCgpgYGB7cn0KIyBTZWxlY3QgY29sdW1ucwpzZWxlY3QoYm5hbWVzLCBzdGFydHNfd2l0aCgic291bmQiKSkKIyBSZW5hbWUgY29sdW1uCnJlbmFtZShpcmlzLCBwZXRhbF9sZW5ndGggPSBQZXRhbC5MZW5ndGgpCmBgYAoKYGBge3J9CiMgc29ydAphcnJhbmdlKGJuYW1lcywgZGVzYyhwcm9wKSlbMyxdCiMgeWVhciB3aGVyZSBRdWVudGluIHdhcyBtb3N0IHBvcHVsYXIKYXJyYW5nZShmaWx0ZXIoYm5hbWVzLCBuYW1lID09ICJRdWVudGluIiksIGRlc2MocHJvcCkpWzEsXQpgYGAKCmBgYHtyfQojIGFkZCBjb2x1bW5zCm11dGF0ZShiaXJ0aHMsIGRvdWJsZSA9IDIgKiBiaXJ0aHMpCiMgdHJhbnNtdXRlIHRvIGRlbGV0ZSBvbGQgY29sdW1ucwpgYGAKCmBgYHtyfQojIFN1bW1hcml6ZQpzdW1tYXJpc2Uodml2aWFuLG1pbiA9IG1pbihwcm9wKSxtZWFuID0gbWVhbihwcm9wKSwgbWF4ID0gbWF4KHByb3ApLCBudW1iZXIgPSBuKCksIG51bWJlcl9kaXN0aW5jdCA9IG5fZGlzdGluY3QocHJvcCkpCmBgYAoKYGBge3J9CgpibmFtZXMyID0gbGVmdF9qb2luKGJuYW1lcywgYmlydGhzLCBieSA9IGMoInllYXIiLCJzZXgiKSkKYm5hbWVzMiA9IG11dGF0ZShibmFtZXMyLCBuID0gcm91bmQocHJvcCAqIGJpcnRocykpCiMgR3JvdXAKYnlfbmFtZSA9IGdyb3VwX2J5KGJuYW1lczIsIG5hbWUpCmJ5X25hbWUKdG90YWxzID0gc3VtbWFyaXNlKGJ5X25hbWUsIHRvdGFsID0gc3VtKG4pKSAKdG90YWxzCmBgYAoKYGBge3J9CmJuYW1lczIKbmFtZV9zZXggPSBncm91cF9ieShibmFtZXMyLCBuYW1lLCB5ZWFyKSAKdG90YWxzMiA9IHN1bW1hcmlzZShuYW1lX3NleCwgdG90YWwgPSBzdW0obikpIApoZWFkKHRvdGFsczIpCmBgYAoKYGBge3J9CmFycmFuZ2UoYm5hbWVzMiwgbmFtZSlbMTozLF0KYGBgCgpgYGB7cn0KdW5ncm91cChieV9uYW1lKQpgYGAKCmBgYHtyfQojIG90aGVyIGV4YW1wbGUKeWVhcl9zZXggPSBncm91cF9ieShibmFtZXMyLCB5ZWFyLCBzZXgpIAp5dG90YWxzID0gc3VtbWFyaXNlKHllYXJfc2V4LCBiaXJ0aHMgPSBzdW0obikpIAp5dG90YWxzCmBgYAoKYGBge3J9CmJuYW1lczIKYGBgCgpgYGB7cn0KIyBJU0xSIAojIENoYXAgNiBSaWRnZSByZWdyZXNzaW9uIC8gTGFzc28KIyBFeCA4CnNldC5zZWVkKDEpClggPSBybm9ybSgxMDApCmVwcyA9IHJub3JtKDEwMCkKCmJldGEwID0gMwpiZXRhMSA9IDIKYmV0YTIgPSAtMwpiZXRhMyA9IDAuMwpZID0gYmV0YTAgKyBiZXRhMSAqIFggKyBiZXRhMiAqIFheMiArIGJldGEzICogWF4zICsgZXBzCgojIFVzZSByZWdzdWJzZXRzIHRvIHNlbGVjdCBiZXN0IG1vZGVsIGhhdmluZyBwb2x5bm9taWFsIG9mIFhYIG9mIGRlZ3JlZSAxMApsaWJyYXJ5KGxlYXBzKQpkYXRhLmZ1bGwgPSBkYXRhLmZyYW1lKHkgPSBZLCB4ID0gWCkKbW9kLmZ1bGwgPSByZWdzdWJzZXRzKHkgfiBwb2x5KHgsIDEwLCByYXcgPSBUKSwgZGF0YSA9IGRhdGEuZnVsbCwgbnZtYXggPSAxMCkKbW9kLnN1bW1hcnkgPSBzdW1tYXJ5KG1vZC5mdWxsKQoKIyBGaW5kIHRoZSBtb2RlbCBzaXplIGZvciBiZXN0IGNwLCBCSUMgYW5kIGFkanIyCndoaWNoLm1pbihtb2Quc3VtbWFyeSRjcCkKd2hpY2gubWluKG1vZC5zdW1tYXJ5JGJpYykKd2hpY2gubWF4KG1vZC5zdW1tYXJ5JGFkanIyKQoKIyBQbG90IGNwLCBCSUMgYW5kIGFkanIyCnBsb3QobW9kLnN1bW1hcnkkY3AsIHhsYWIgPSAiU3Vic2V0IFNpemUiLCB5bGFiID0gIkNwIiwgcGNoID0gMjAsIHR5cGUgPSAibCIpCnBvaW50cygzLCBtb2Quc3VtbWFyeSRjcFszXSwgcGNoID0gNCwgY29sID0gInJlZCIsIGx3ZCA9IDcpCnBsb3QobW9kLnN1bW1hcnkkYmljLCB4bGFiID0gIlN1YnNldCBTaXplIiwgeWxhYiA9ICJCSUMiLCBwY2ggPSAyMCwgdHlwZSA9ICJsIikKcG9pbnRzKDMsIG1vZC5zdW1tYXJ5JGJpY1szXSwgcGNoID0gNCwgY29sID0gInJlZCIsIGx3ZCA9IDcpCnBsb3QobW9kLnN1bW1hcnkkYWRqcjIsIHhsYWIgPSAiU3Vic2V0IFNpemUiLCB5bGFiID0gIkFkanVzdGVkIFIyIiwgcGNoID0gMjAsIAogICAgdHlwZSA9ICJsIikKcG9pbnRzKDMsIG1vZC5zdW1tYXJ5JGFkanIyWzNdLCBwY2ggPSA0LCBjb2wgPSAicmVkIiwgbHdkID0gNykKCiMgQ29lZnMgZm91bmQgYnkgcmVncmVzc2lvbiAocmVwbGFjZXMgeF4zIGJ5IHheNykKY29lZmZpY2llbnRzKG1vZC5mdWxsLCBpZCA9IDMpCgpgYGAKCmBgYHtyfQojIE5vdyBsYXNzbwpsaWJyYXJ5KGdsbW5ldCkKeG1hdCA9IG1vZGVsLm1hdHJpeCh5IH4gcG9seSh4LCAxMCwgcmF3ID0gVCksIGRhdGEgPSBkYXRhLmZ1bGwpWywgLTFdICMgcmVtb3ZlIGludGVyY2VwdCAoZmlyc3QgY29sdW1uKQptb2QubGFzc28gPSBjdi5nbG1uZXQoeG1hdCwgWSwgYWxwaGEgPSAxKSAjIGN2LmdsbW5ldDogY3Jvc3MgdmFsaWRhdGlvbiB0byBzZWxlY3QgYmVzdCBsYW1iZGEKYmVzdC5sYW1iZGEgPSBtb2QubGFzc28kbGFtYmRhLm1pbgpiZXN0LmxhbWJkYQpwbG90KG1vZC5sYXNzbykKCiMgTmV4dCBmaXQgdGhlIG1vZGVsIG9uIGVudGlyZSBkYXRhIHVzaW5nIGJlc3QgbGFtYmRhCmJlc3QubW9kZWwgPSBnbG1uZXQoeG1hdCwgWSwgYWxwaGEgPSAxKQpwcmVkaWN0KGJlc3QubW9kZWwsIHMgPSBiZXN0LmxhbWJkYSwgdHlwZSA9ICJjb2VmZmljaWVudHMiKQojTGFzc28gYWxzbyBwaWNrcyBYXjUgb3ZlciBYXjMuIEl0IGFsc28gcGlja3MgWF43IHdpdGggbmVnbGlnaWJsZSBjb2VmZmljaWVudC4KCmBgYAoKYGBge3J9CiMgSVNMUiAKIyBDaGFwIDcgTm9uLWxpbmVhciBNb2RlbGluZyAtIFNwbGluZXMsIEdBTQpsaWJyYXJ5KElTTFIpCmF0dGFjaChXYWdlKQpgYGAKCmBgYHtyfQpmaXQ9bG0od2FnZX5wb2x5KGFnZSw0LHJhdz1UKSxkYXRhPVdhZ2UpICAjIHJhdz1UIHRvIG9idGFpbiBjb2VmZmljaWVudHMgb2YgcG9seSBkaXJlY3RseQpjb2VmKHN1bW1hcnkoZml0KSkgIyBiZWxvdyB3ZSBzZWUgc21hbGwgY29lZiAoYW5kIHAtdmFsdWUpIGZvciBvcmRlciAzICYgNAoKIyBFcXVpdmFsZW50IGV4cHJlc3Npb25zCmZpdDJhPWxtKHdhZ2V+YWdlK0koYWdlXjIpK0koYWdlXjMpK0koYWdlXjQpLGRhdGE9V2FnZSkKY29lZihmaXQyYSkKZml0MmI9bG0od2FnZX5jYmluZChhZ2UsYWdlXjIsYWdlXjMsYWdlXjQpLGRhdGE9V2FnZSkgIyBjYmluZCBpbiBmb3JtdWxhcyA8LT4gSSgpIHdyYXBwZXIKY29lZihmaXQyYikKYGBgCgpgYGB7cn0KIyBQcmVkaWN0aW9uCmFnZWxpbXM9cmFuZ2UoYWdlKQphZ2UuZ3JpZD1zZXEoZnJvbT1hZ2VsaW1zWzFdLHRvPWFnZWxpbXNbMl0pCnByZWRzPXByZWRpY3QoZml0LG5ld2RhdGE9bGlzdChhZ2U9YWdlLmdyaWQpLHNlPVRSVUUpICMgY3JlYXRlcyBsaXN0IG9mIGFnZXMgd2hlcmUgd2Ugd2FudCBwcmVkaWN0aW9uIChzZWUgYmVsb3cpCnNlLmJhbmRzPWNiaW5kKHByZWRzJGZpdCsyKnByZWRzJHNlLmZpdCxwcmVkcyRmaXQtMipwcmVkcyRzZS5maXQpCnBhcihtZnJvdz1jKDEsMiksbWFyPWMoNC41LDQuNSwxLDEpLG9tYT1jKDAsMCw0LDApKSAjIG1hci8gb21hOiBtYXJnaW5zIG9mIHBsb3QKcGxvdChhZ2Usd2FnZSx4bGltPWFnZWxpbXMsY2V4PS41LGNvbD0iZGFya2dyZXkiKQp0aXRsZSgiRGVncmVlLTQgUG9seW5vbWlhbCIsb3V0ZXI9VCkKbGluZXMoYWdlLmdyaWQscHJlZHMkZml0LGx3ZD0yLGNvbD0iYmx1ZSIpCm1hdGxpbmVzKGFnZS5ncmlkLHNlLmJhbmRzLGx3ZD0xLGNvbD0iYmx1ZSIsbHR5PTMpCmBgYAoKYGBge3J9CmFnZS5ncmlkPXNlcShmcm9tPWFnZWxpbXNbMV0sdG89YWdlbGltc1syXSkgIyBPcjogYWdlLmdyaWQyPXNvcnQodW5pcXVlKGFnZSkpCmxpc3QoYWdlPWFnZS5ncmlkKSAjICRhZ2UgbGlzdAoKcHJlZGljdChmaXQsbGlzdChhZ2U9Yyg3MCkpKSAjIHRlc3QgZm9yIDEgYWdlIC0gc3RpbGwgbmVlZCB0byBjcmVhdGUgYW4gJGFnZSBsaXN0CmBgYAoKYGBge3J9CnByZWRzCmBgYAoKYGBge3J9CiMgVXNlIG9mIEFOT1ZBID0gQW5hbHlzaXMgb2YgdmFyaWFuY2UuIFRvIGRldGVybWluZWQgd2hpY2ggZGVncmVlIGlzIG5lZWRlZApmaXQuMT1sbSh3YWdlfmFnZSxkYXRhPVdhZ2UpCmZpdC4yPWxtKHdhZ2V+cG9seShhZ2UsMiksZGF0YT1XYWdlKQpmaXQuMz1sbSh3YWdlfnBvbHkoYWdlLDMpLGRhdGE9V2FnZSkKZml0LjQ9bG0od2FnZX5wb2x5KGFnZSw0KSxkYXRhPVdhZ2UpCmZpdC41PWxtKHdhZ2V+cG9seShhZ2UsNSksZGF0YT1XYWdlKQphbm92YShmaXQuMSxmaXQuMixmaXQuMyxmaXQuNCxmaXQuNSkgCiMgT3Igb2J0YWluIHRoZXNlIHAtdmFsdWVzIGJ5IGV4cGxvaXRpbmcgdGhlIGZhY3QgdGhhdCBwb2x5KCkgY3JlYXRlcyBvcnRob2dvbmFsIHBvbHlub21pYWxzOgpjb2VmKHN1bW1hcnkoZml0LjUpKSAjIGFuZCBjaGVjayBQLXZhbHVlcy4gRWFjaCBwLXZhbHVlIGNvcnJlc3BvbmRzIHRvIGNvbXBhcmlzb24gZnJvbSBtb2RlbCB3IHByZXZpb3VzIG9uZQpgYGAKCmBgYHtyfQojIEdlbmVyYWxpemVkIGxpbmVhciBmdW5jdGlvbgpmaXQ9Z2xtKEkod2FnZT4yNTApfnBvbHkoYWdlLDQpLGRhdGE9V2FnZSxmYW1pbHk9Ymlub21pYWwpICMgcGx1cyBjcmVhdGUgYmluYXJ5IHJlc3BvbnNlIG9uIHRoZSBmbHkgd2l0aCBJKCkKcHJlZHM9cHJlZGljdChmaXQsbmV3ZGF0YT1saXN0KGFnZT1hZ2UuZ3JpZCksc2U9VCkgIyBkZWZhdWx0OiB0eXBlPSJsaW5rIiA9IHByZWRpY3Rpb25zIGZvciBsb2dpdAojIG90aGVyIG9wdGlvbiB0eXBlPSJyZXNwb25zZSIgKHByb2JhYmlsaXR5IHZzIG9kZHMpCmBgYAoKYGBge3J9CiMgU3RlcCBmdW5jdGlvbgp0YWJsZShjdXQoYWdlLDQpKQpmaXQ9bG0od2FnZX5jdXQoYWdlLDQpLGRhdGE9V2FnZSkgIyBjdXQoKSByZXR1cm5zIGFuIG9yZGVyZWQgY2F0ZWdvcmljYWwgdmFyaWFibGUuIEJyZWFrcz0gY2FuIGJlIHVzZWQgCmNvZWYoc3VtbWFyeShmaXQpKQoKYGBgCgpgYGB7cn0KZml0LjE9bG0od2FnZX5lZHVjYXRpb24rYWdlLGRhdGE9V2FnZSkKZml0LjI9bG0od2FnZX5lZHVjYXRpb24rcG9seShhZ2UsMiksZGF0YT1XYWdlKQpmaXQuMz1sbSh3YWdlfmVkdWNhdGlvbitwb2x5KGFnZSwzKSxkYXRhPVdhZ2UpCmFub3ZhKGZpdC4xLGZpdC4yLGZpdC4zKSAjIEFub3ZhIGNhbiBjb21wYXJlIG5vdCBvcnRob2dvbmFsIHBvbHlub21pYWxzLCBhbmQgdGhhdCBoYXZlIG90aGVyIHRlcm1zCmBgYAoKYGBge3J9CiMgU3BsaW5lcwpsaWJyYXJ5KHNwbGluZXMpCmZpdD1sbSh3YWdlfmJzKGFnZSxrbm90cz1jKDI1LDQwLDYwKSksZGF0YT1XYWdlKSAjIDMga25vdHMgLS0+IDcgZGVncmVlcyBvZiBmcmVlZG9tIC0tPiAxIGludGVyY2VwdCArIDYgYmFzaXMgZnVuY3Rpb25zCnByZWQ9cHJlZGljdChmaXQsbmV3ZGF0YT1saXN0KGFnZT1hZ2UuZ3JpZCksc2U9VCkKcGxvdChhZ2Usd2FnZSxjb2w9ImdyYXkiKQpsaW5lcyhhZ2UuZ3JpZCxwcmVkJGZpdCxsd2Q9MikKbGluZXMoYWdlLmdyaWQscHJlZCRmaXQrMipwcmVkJHNlLGx0eT0iZGFzaGVkIikKbGluZXMoYWdlLmdyaWQscHJlZCRmaXQtMipwcmVkJHNlLGx0eT0iZGFzaGVkIikKYGBgCgpgYGB7cn0KcHJlZD1wcmVkaWN0KGZpdCxzZT1UKQpwbG90KGFnZSx3YWdlLGNvbD0iZ3JheSIpCmxpbmVzKGFnZSxwcmVkJGZpdCxsd2Q9MikgIyB0b28gbWFueSBwb2ludHM6IHRoYXQncyB3aHkgd2UgY2hhbmdlZCBhZ2UgaW50byBhIGxpc3Qgb2YgYWxsIHVuaXF1ZSBhZ2VzCmBgYAoKYGBge3J9CiMgVGhhdCB3b3JrcyB0b28gLSBqdXN0IG5lZWQgdG8gaGF2ZSBzYW1lIGxlbmd0aCBvZiBkYXRhIGZvciB4IGFuZCB5IGluIGxpbmVzKHgsIHkuLi4pCiMgPSBiZXd0d2VlbiBhZ2UuZ3JpZDIgYW5kIHByZWQyJGZpdAphZ2UuZ3JpZDI9c29ydCh1bmlxdWUoYWdlKSkKcHJlZDI9cHJlZGljdChmaXQsbmV3ZGF0YT1saXN0KGFnZT1hZ2UuZ3JpZDIpLHNlPVQpCnBsb3QoYWdlLHdhZ2UsY29sPSJncmF5IikKbGluZXMoYWdlLmdyaWQyLHByZWQyJGZpdCxsd2Q9MikKYGBgCgpgYGB7cn0Kc3RyKHByZWQyKQpgYGAKCmBgYHtyfQpkaW0oYnMoYWdlLGtub3RzPWMoMjUsNDAsNjApLCBkZWdyZWUgPSAzKSkgIyBicyBnZW5lcmF0ZXMgbWF0cml4IHdpdGggNiBiYXNpcyBmdW5jdGlvbnMgKHNlZSBiZWxvdykKZGltKGJzKGFnZSxkZj02KSkgIyBkZiBvcHRpb24gdG8gcHJvZHVjZSBhIHNwbGluZSB3aXRoIGtub3RzIGF0IHVuaWZvcm0gcXVhbnRpbGVzIG9mIHRoZSBkYXRhCmF0dHIoYnMoYWdlLGRmPTYpLCJrbm90cyIpCmBgYAoKYGBge3J9CiMgTmF0dXJhbCBzcGxpbmU6IG5zKCkKZml0Mj1sbSh3YWdlfm5zKGFnZSxkZj00KSxkYXRhPVdhZ2UpCnByZWQyPXByZWRpY3QoZml0MixuZXdkYXRhPWxpc3QoYWdlPWFnZS5ncmlkKSxzZT1UKSAjbGlzdCBvciBkYXRhLmZyYW1lIGZvciBwcmVkaXQoPGNsYXNzIGxtPiwgLi4pCnBsb3QoYWdlLHdhZ2UseGxpbT1hZ2VsaW1zLGNleD0uNSxjb2w9ImRhcmtncmV5IikKbGluZXMoYWdlLmdyaWQsIHByZWQyJGZpdCxjb2w9InJlZCIsbHdkPTIpCmBgYAoKYGBge3J9CiMgU21vb3RoIHNwbGluZQpwbG90KGFnZSx3YWdlLHhsaW09YWdlbGltcyxjZXg9LjUsY29sPSJkYXJrZ3JleSIpCnRpdGxlKCJTbW9vdGhpbmcgU3BsaW5lIikKZml0PXNtb290aC5zcGxpbmUoYWdlLHdhZ2UsZGY9MTYpICMgZm9yY2VzIDE2IGRlZ3JlZXMgb2YgZnJlZWRvbQpmaXQyPXNtb290aC5zcGxpbmUoYWdlLHdhZ2UsY3Y9VFJVRSkgIyBjaG9vc2UgdmFsdWUgb2YgbGFtYmRhIGJ5IGNyb3NzIHZhbGlkYXRpb24gLS0+IDYuOCBkZWdyZWVzIG9mIGZyZWVkb20KZml0MiRkZgpsaW5lcyhmaXQsY29sPSJyZWQiLGx3ZD0yKQpsaW5lcyhmaXQyLGNvbD0iYmx1ZSIsbHdkPTIpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCIxNiBERiIsIjYuOCBERiIpLGNvbD1jKCJyZWQiLCJibHVlIiksbHR5PTEsbHdkPTIsY2V4PS44KQpgYGAKCmBgYHtyfQojIExvY2FsIHJlZ3Jlc3NzaW9uIChMb2VzcykKcGxvdChhZ2Usd2FnZSx4bGltPWFnZWxpbXMsY2V4PS41LGNvbD0iZGFya2dyZXkiKQp0aXRsZSgiTG9jYWwgUmVncmVzc2lvbiIpCmZpdD1sb2Vzcyh3YWdlfmFnZSxzcGFuPS4yLGRhdGE9V2FnZSkgIyBzcGFuPTAuMiBtZWFucyB1c2UgMjAlIG9mIG9ic2VydmF0aW9ucwpmaXQyPWxvZXNzKHdhZ2V+YWdlLHNwYW49LjUsZGF0YT1XYWdlKSAjIDUwJSBvZiBvYnNlcnZhdGlvbnMgLS0+IHNtb290aGVyCmxpbmVzKGFnZS5ncmlkLHByZWRpY3QoZml0LGRhdGEuZnJhbWUoYWdlPWFnZS5ncmlkKSksY29sPSJyZWQiLGx3ZD0yKSAjIHByZWRpY3QgbmVlZHMgZGF0YS5mcmFtZSBmb3IgbG9lc3MgZml0CmxpbmVzKGFnZS5ncmlkLHByZWRpY3QoZml0MixkYXRhLmZyYW1lKGFnZT1hZ2UuZ3JpZCkpLGNvbD0iYmx1ZSIsbHdkPTIpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCJTcGFuPTAuMiIsIlNwYW49MC41IiksY29sPWMoInJlZCIsImJsdWUiKSxsdHk9MSxsd2Q9MixjZXg9LjgpCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoK